Initialize environment

library(caim)
caim::clr()

Setup preferences

# most important maturities
maturities_a = c(.25, 2, 5, 10)
# interesting maturities, level b
maturities_b = c(.0833, 1, 30)
# all other maturities will be level c

# relative weightings for each maturity level
# these will form a density function that will be normalised to sum to 1
#   , so don't worry about that here
MAT_A_WT = 1
MAT_B_WT = .75
MAT_C_WT = 0.15

# set of maturities to use for lambda values
lambda_maturities <- seq(0.5, 5, 0.5)

# k for k-fold evaluation
NUM_K_FOLDS = 10

Load G7 yield info

g7_rates_info <- fread("../../data/rates_info_now.csv")[
  country %in% c("US", "CA", "DE", "FR", "IT", "UK", "JP") & class=="GOVT"
  , .(country, class, maturity, bb_ticker)
]

g7_rates_data <- g7_rates_info[
  fread("../../data/rates_data_now.csv")
  , 
  , on=.(bb_ticker), nomatch=NULL 
][
  , .(country
      , class
      , maturity
      , date=lubridate::as_date(date)
      , year=lubridate::year(lubridate::as_date(date))
      , month=lubridate::month(lubridate::as_date(date))
      , yield=PX_LAST
      )
][
  , .SD, key=.(country, class, maturity, date)
]


g7_curve_mats <- sort(unique(g7_rates_data$maturity))
g7_curve_columns <- paste0("yield_", g7_curve_mats)

g7_curves <- dcast(
  g7_rates_data, country + class + date + year + month ~ maturity, value.var="yield"  
)[
  , .SD
  , key=.(country, class, date)  
]
setnames(g7_curves, c(key(g7_curves), g7_curve_mats), c(key(g7_curves), g7_curve_columns))

g7_curves[]

Load G7 Money market data

g7_mm_info <- data.table(
  country=c("US", "CA", "DE", "FR", "IT", "UK", "JP", "DE", "FR", "IT")
  , curncy=c("USD", "CAD", "EUR", "EUR", "EUR", "GBP", "JPY", "DEM", "FRF", "ITL")
  , id=c("USD.FIX.1M", "CAD.FIX.1M", "EUR.FIX.1M", "EUR.FIX.1M", "EUR.FIX.1M"
         , "GBP.FIX.1M", "JPY.FIX.1M", "DEM.FIX.1M", "FRF.FIX.1M", "ITL.FIX.1M")
)
# g7_mm_info <- fread("../data/mminfo.csv")[
#   CURNCY %in% c("USD",  "EUR", "GBP", "JPY", "CAD")
#   , .(curncy=CURNCY, id=FIX_1M)
# ]
g7_mm_data <- g7_mm_info[
  fread("../../data/mmdata.csv")
  , 
  , on=.(id), nomatch=NULL
][
  , .(country
      , curncy
      , date = lubridate::as_date(date)
      , year=lubridate::year(lubridate::as_date(date))
      , month=lubridate::month(lubridate::as_date(date))
      , maturity = 0.8333
      , yield = PX_LAST
      )
][
  , .SD , key=.(country, curncy, date)
]

# stitch together pre- and post-EUR convergence data for DE, FR, IT
eur_start_date <- min(g7_mm_data[country=="DE" & curncy=="EUR"]$date)
# delete local observations where EUR data is available
g7_mm_data <- g7_mm_data[!(curncy %in% c("DEM", "FRF", "ITL") & date >= eur_start_date)]
# relabel pre-EUR local observations as EUR
# may need to rethink this if we want to implement pre- and post-EUR currency data
# - maybe label everything as DEM FRF ITL?
g7_mm_data[curncy %in% c("DEM", "FRF", "ITL"), curncy := "EUR"] 

g7_mm_data[]

Create monthly data

g7_curves_m <- g7_curves[
  date %in% caim::month_end_dates(date)
]

g7_mm_data_m <- g7_mm_data[
  date %in% caim::month_end_dates(date)
]

Add money market data to short end of government curves

This is, admittedly, a big kludge, but should help when there are no government yields below 2-year maturity, like early Canada, etc. Also, this may be a good way to average 3-month bills and 1-month deposits as a proxy for cash when doing Nelson-Siegel analyses.

Assumption: We’ll do LIBOR - 1/8 as a crude proxy for LIBID

g7_curves_m[g7_mm_data_m, yield_0.0833 := i.yield - 0.125, on=.(country, year, month)][]

Ensure there are at least 3 yield points

# ensure there are at least 3 yield points
g7_curves_m <- g7_curves_m[
  g7_curves_m[ ,.(valid=(sum(!is.na(.SD)) >= 3)), by=.(country, class, date)]$valid
][
  # find first date that works for all countries
  date >= max(g7_curves_m[,.(min_date=min(date)), by=.(country, class)]$min_date)
]

Calculate Nelson Siegel coefficients

curve_data <- g7_curves_m
curve_mats <- g7_curve_mats
curve_columns <- g7_curve_columns

curve_coefs <- list()
curve_ids <- unique(curve_data[,.(country, class)])
for (c in 1:nrow(curve_ids)) {
  t_curve_data <- curve_data[country == curve_ids[c, country] & class == curve_ids[c, class]]
  t_curve_dates <- sort(unique(t_curve_data$date))
  t_curve_mats <- curve_mats #sort(unique(yield_info[curve_id==c]$maturity))
  t_curve_mat_names <- curve_columns #as.character(t_curve_mats)
  
  t_curve_mat_wts <- rep(MAT_C_WT, length(t_curve_mats))
  names(t_curve_mat_wts) <- t_curve_mat_names
  t_curve_mat_wts[names(t_curve_mat_wts) %in% maturities_a] <- MAT_A_WT
  t_curve_mat_wts[names(t_curve_mat_wts) %in% maturities_b] <- MAT_B_WT
  t_curve_mat_wts <- t_curve_mat_wts / sum(t_curve_mat_wts)
  
  
  t_res <- list()
  for (i in 1:length(lambda_maturities)) {
    mat <- lambda_maturities[i]
    lambda <- caim::ns_mat2lambda(mat)
    print(paste("calculating for curve:", c, curve_ids[c, country], curve_ids[c, class], "maturity:", mat, "lambda:", lambda))
    t_res[[i]] <- list(
      mat=mat
      , lambda=lambda
      , coefs=data.table(
        country=curve_ids[c, country]
        , class=curve_ids[c, class]
        , date=t_curve_data[,date]
        , t(apply(t_curve_data, 1, function(x) 
          caim::ns_yields2coefs(t_curve_mats
                                , as.numeric(x[t_curve_mat_names])
                                , lambda=lambda
                                , wts=t_curve_mat_wts)))
        , lambda_mat=mat
        )
      )
  }
  
  # calculate summaries
  # 5 year halflife assuming 260 business days in a year
  decay <- halflife2decay(5*260)
  lambda_summary <- data.table(t(sapply(t_res, function(x) 
    c(mat=x$mat, lambda=x$lambda, wss=mean(x$coefs$wss), wss_exp=mean_exp(x$coefs$wss, decay)))))

  # choose best data
  best_hist_fit <- which(lambda_summary$wss == min(lambda_summary$wss))
  best_exp_fit <- which(lambda_summary$wss_exp == min(lambda_summary$wss_exp))
  # take average of best fits, but tilt towards best_exp_fit
  best_ix <- floor(mean(c(best_hist_fit, best_exp_fit))+ifelse(best_exp_fit > best_hist_fit, 0.5, 0))
  best_hist_coefs <- t_res[[best_ix]]$coefs
  # best_hist_coefs$curve_id <- c

  #add data to curve_coefs
  curve_coefs[[length(curve_coefs)+1]] <- best_hist_coefs
}
[1] "calculating for curve: 1 CA GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 1 CA GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 1 CA GOVT maturity: 1.5 lambda: 1.19552075559799"
[1] "calculating for curve: 1 CA GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 1 CA GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 1 CA GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 1 CA GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 1 CA GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 1 CA GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 1 CA GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 2 DE GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 2 DE GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 2 DE GOVT maturity: 1.5 lambda: 1.19552075559799"
[1] "calculating for curve: 2 DE GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 2 DE GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 2 DE GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 2 DE GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 2 DE GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 2 DE GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 2 DE GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 3 FR GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 3 FR GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 3 FR GOVT maturity: 1.5 lambda: 1.19552075559799"
[1] "calculating for curve: 3 FR GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 3 FR GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 3 FR GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 3 FR GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 3 FR GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 3 FR GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 3 FR GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 4 IT GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 4 IT GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 4 IT GOVT maturity: 1.5 lambda: 1.19552075559799"
[1] "calculating for curve: 4 IT GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 4 IT GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 4 IT GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 4 IT GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 4 IT GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 4 IT GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 4 IT GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 5 JP GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 5 JP GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 5 JP GOVT maturity: 1.5 lambda: 1.19552075559799"
[1] "calculating for curve: 5 JP GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 5 JP GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 5 JP GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 5 JP GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 5 JP GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 5 JP GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 5 JP GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 6 UK GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 6 UK GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 6 UK GOVT maturity: 1.5 lambda: 1.19552075559799"
[1] "calculating for curve: 6 UK GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 6 UK GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 6 UK GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 6 UK GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 6 UK GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 6 UK GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 6 UK GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 7 US GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 7 US GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 7 US GOVT maturity: 1.5 lambda: 1.19552075559799"
[1] "calculating for curve: 7 US GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 7 US GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 7 US GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 7 US GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 7 US GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 7 US GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 7 US GOVT maturity: 5 lambda: 0.358681854090179"
ns_coefs <- rbindlist(curve_coefs)[
  , .(ns_beta0 = beta0
      , ns_beta1 = beta1
      , ns_beta2 = beta2
      , ns_lambda = lambda
      , ns_lambda_mat = lambda_mat
      , ns_wss = wss
      )
  , key=.(country, class, date)
]

g7_curves_m <- g7_curves_m[ns_coefs]

rm(list=setdiff(ls(), c("NUM_K_FOLDS", "g7_curves", "g7_curves_m", "g7_rates_data", "g7_rates_info", "g7_curve_columns", "g7_curve_mats")))

Yield calculations

long_data <- melt(
  g7_curves_m  
  , c("country", "class", "date", "ns_beta0", "ns_beta1", "ns_beta2", "ns_lambda")
  ,  patterns("yield_")
  , value.name="yield_now"
)[
  , maturity := as.numeric(stringr::str_remove(variable, "yield_"))
][
  , .(country, class, date, ns_beta0, ns_beta1, ns_beta2, ns_lambda, maturity, yield_now)
]

# fill in missing yields with interpolated data
# table with !is.na(yield)
yields_valid <- long_data[!is.na(yield_now)][, c("mat", "yld") := .(maturity, yield_now)]
# table mapping to <=
yields_lo <- yields_valid[long_data, .(country, date, maturity, m_lo0=mat, y_lo0=yld), on=.(country, date, maturity), roll=T]
# table mapping to >=
yields_hi <- yields_valid[long_data, .(country, date, maturity, m_hi0=mat, y_hi0=yld), on=.(country, date, maturity), roll=-Inf]

yields_lin <- yields_hi[
  yields_lo
  , .(
    country
    , date
    , maturity
    , yield_lin = ifelse(m_hi0 == m_lo0
                         , y_hi0
                         , y_lo0 + (maturity - m_lo0) * (y_hi0 - y_lo0) / (m_hi0 - m_lo0)
                         )
    , y_hi0
    , y_lo0
    , m_hi0
    , m_lo0
  )
  , on=.(country, date, maturity)
]

long_data <- long_data[yields_lin, on=.(country, date, maturity)]

long_data <- long_data[
  , c("num_coups", "mod_dur") := .(
    ifelse(maturity > 0.25, ifelse(country=="US", 2, 1), 0)
    , maturity
  )  
][
  maturity > 0.25
  , mod_dur := round(caim::modified_duration(yield_lin / 100, yield_lin / 100, maturity, num_coups), 6)
][
  , .(
    ns_beta0
    , ns_beta1
    , ns_beta2
    , ns_lambda
    , num_coups
    , mod_dur #= round(caim::modified_duration(yield_now / 100, yield_now / 100, maturity, 1), 6)
    , yield_now
    , yield_lin
    , y_hi0
    , y_lo0
    , m_hi0
    , m_lo0
    )
  , key=.(country, class, maturity, date)
][
  , yield_prev := shift(yield_lin), by = .(country, class, maturity)
][
  , c("y_hi1", "y_lo1", "m_hi1", "m_lo1") := .(
    yield_lin
    , shift(yield_lin)
    , maturity
    , shift(maturity)
  )
  , by = .(country, class, date)
][
  , yield_sell_lin := (
    y_lo1 + ((m_hi1 - 1/12) - m_lo1) * (y_hi1 - y_lo1) / (m_hi1 - m_lo1)
  )
][
  , yield_buy_ns := round(caim::ns_coefs2yields(
    mats = maturity
    , beta0 = shift(ns_beta0)
    , beta1 = shift(ns_beta1)
    , beta2 = shift(ns_beta2)
    , lambda = shift(ns_lambda)
  )$y, 4)
  , by = .(country, class, maturity)
][
  , yield_sell_ns := round(caim::ns_coefs2yields(
    mats = maturity - 1/2
    , beta0 = ns_beta0
    , beta1 = ns_beta1
    , beta2 = ns_beta2
    , lambda = ns_lambda
  )$y, 4)
][
  , coup_inc_lin := round(yield_prev / 1200, 6)
][
  , shift_inc := ifelse(maturity > 0.25, round((yield_lin - yield_prev) / 100 * -mod_dur, 6), 0)
][
  , tot_ret_shift := coup_inc_lin + shift_inc
][
  , dur_inc_lin := ifelse(
    maturity > 0.25
    , round(caim::bond_price(yield_sell_lin / 100, yield_prev / 100, maturity - 1/12, 1, 1) - 1, 6)
    , 0
    )
][
  , tot_ret_lin := coup_inc_lin + dur_inc_lin
][
  , coup_inc_ns := round(yield_buy_ns / 1200, 6)  
][
 , price_inc_ns := ifelse(
   maturity > 0.25
   , round(caim::bond_price(yield_sell_ns / 100, yield_buy_ns / 100, maturity - 1/12, 1, 1) - 1, 6)
   , 0
   )
][
  , tot_ret_ns := coup_inc_ns + price_inc_ns
]

long_data[country=="US"]

wide_data <- dcast(
  long_data
  ,country + class + date + ns_beta0 + ns_beta1 + ns_beta2 + ns_lambda ~ maturity
  , value.var = names(long_data)[!(names(long_data) %in% c("country", "class", "date", "ns_beta0", "ns_beta1", "ns_beta2", "ns_lambda", "maturity"))]

  # , value.var = c("mod_dur", "yield_now", "yield_prev", "yield_buy_ns", "yield_sell_ns"
  #                 , "coup_inc_lin", "dur_inc_lin", "coup_inc_ns", "price_inc_ns", "tot_ret_ns"
  #                 )
  )

wide_data[]

g7_return_data <- dcast(
  long_data[maturity %in% c(0.0833, 1, 2, 3, 5, 7, 10)]
  , country + class + date ~ maturity
  , value.var = c("coup_inc_lin", "tot_ret_shift", "tot_ret_lin", "tot_ret_ns")
)

g7_return_data[]

Get asset data

g7_assets <- data.table(
  id=c("US.GOVT.13", "US.GOVT.15", "US.GOVT.110"
       , "CA.GOVT.13", "CA.GOVT.15", "CA.GOVT.110"
       , "DE.GOVT.13", "DE.GOVT.15", "DE.GOVT.110"
       , "FR.GOVT.13", "FR.GOVT.15", "FR.GOVT.110"
       , "IT.GOVT.13", "IT.GOVT.15", "IT.GOVT.110"
       , "UK.GOVT.13", "UK.GOVT.15", "UK.GOVT.110"
       , "JP.GOVT.13", "JP.GOVT.15", "JP.GOVT.110"
       )
  , country=c(rep("US", 3), rep("CA", 3), rep("DE", 3), rep("FR", 3), rep("IT", 3)
              , rep("UK", 3), rep("JP", 3))
  , maturity=rep(c(13, 15, 110), 7)
  , key=c("country", "maturity")
)
g7_asset_info <- fread("../../data/assetinfo.csv")[suggname %in% g7_assets$id]

g7_asset_info[]

g7_asset_data <- g7_assets[
  fread("../../data/assetdata.csv")
  , .(
    country
    , maturity
    , id
    , date = lubridate::as_date(date)
    , year = lubridate::year(date)
    , month = lubridate::month(date)
    , index_nav=PX_LAST
    )
  , on=.(id), nomatch=NULL
][
  date %in% caim::month_end_dates(date)
  , .SD
  , key=.(country, maturity, date)
][
  , index_ret := round(index_nav / shift(index_nav) - 1, 6)
  , by=.(country, maturity)
]

g7_asset_data[]

Model Setup

g7_models <- list(
  list(name="x_g13_coup_bullet", type="coup_bullet", maturity=13
       , features=c("coup_inc_lin_2"), unity_coefs=T)
  , list(name="x_g13_shift_bullet", type="shift_bullet", maturity=13
         , features=c("tot_ret_shift_2"), unity_coefs=T)
  , list(name="x_g13_lin_bullet", type="lin_bullet", maturity=13
         , features=c("tot_ret_lin_2"), unity_coefs=T)
  , list(name="x_g13_ns_bullet", type="ns_bullet", maturity=13
         , features=c("tot_ret_ns_2"), unity_coefs=T)
  , list(name="x_g13_coup_n", type="coup_n", maturity=13
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3"), unity_coefs=T)
  , list(name="x_g13_shift_n", type="shift_n", maturity=13
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3"), unity_coefs=T)
  , list(name="x_g13_lin_n", type="lin_n", maturity=13
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3"), unity_coefs=T)
  , list(name="x_g13_ns_n", type="ns_n", maturity=13
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3"), unity_coefs=T)
  , list(name="x_g13_coup_ladder", type="coup_ladder", maturity=13
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3"), unity_coefs=F)
  , list(name="x_g13_shift_ladder", type="shift_ladder", maturity=13
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3"), unity_coefs=F)
  , list(name="x_g13_lin_ladder", type="lin_ladder", maturity=13
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3"), unity_coefs=F)
  , list(name="x_g13_ns_ladder", type="ns_ladder", maturity=13
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3"), unity_coefs=F)

  , list(name="x_g15_coup_bullet", type="coup_bullet", maturity=15
         , features=c("coup_inc_lin_3"), unity_coefs=T)
  , list(name="x_g15_shift_bullet", type="shift_bullet", maturity=15
         , features=c("tot_ret_shift_3"), unity_coefs=T)
  , list(name="x_g15_lin_bullet", type="lin_bullet", maturity=15
         , features=c("tot_ret_lin_3"), unity_coefs=T)
  , list(name="x_g15_ns_bullet", type="ns_bullet", maturity=15
         , features=c("tot_ret_ns_3"), unity_coefs=T)
  , list(name="x_g15_coup_n", type="coup_n", maturity=15
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3", "coup_inc_lin_5"), unity_coefs=T)
  , list(name="x_g15_shift_n", type="shift_n", maturity=15
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3", "tot_ret_shift_5"), unity_coefs=T)
  , list(name="x_g15_lin_n", type="lin_n", maturity=15
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3", "tot_ret_lin_5"), unity_coefs=T)
  , list(name="x_g15_ns_n", type="ns_n", maturity=15
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3", "tot_ret_ns_5"), unity_coefs=T)
  , list(name="x_g15_coup_ladder", type="coup_ladder", maturity=15
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3", "coup_inc_lin_5"), unity_coefs=F)
  , list(name="x_g15_shift_ladder", type="shift_ladder", maturity=15
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3", "tot_ret_shift_5"), unity_coefs=F)
  , list(name="x_g15_lin_ladder", type="lin_ladder", maturity=15
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3", "tot_ret_lin_5"), unity_coefs=F)
  , list(name="x_g15_ns_ladder", type="ns_ladder", maturity=15
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3", "tot_ret_ns_5"), unity_coefs=F)
  
  , list(name="x_g110_coup_bullet", type="coup_bullet", maturity=110
         , features=c("coup_inc_lin_5"), unity_coefs=T)
  , list(name="x_g110_shift_bullet", type="shift_bullet", maturity=110
         , features=c("tot_ret_shift_5"), unity_coefs=T)
  , list(name="x_g110_lin_bullet", type="lin_bullet", maturity=110
         , features=c("tot_ret_lin_5"), unity_coefs=T)
  , list(name="x_g110_ns_bullet", type="ns_bullet", maturity=110
         , features=c("tot_ret_ns_5"), unity_coefs=T)
  , list(name="x_g110_coup_n", type="coup_n", maturity=110
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3", "coup_inc_lin_5", "coup_inc_lin_7", "coup_inc_lin_10")
         , unity_coefs=T)
  , list(name="x_g110_shift_n", type="shift_n", maturity=110
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3", "tot_ret_shift_5", "tot_ret_shift_7", "tot_ret_shift_10")
         , unity_coefs=T)
  , list(name="x_g110_lin_n", type="lin_n", maturity=110
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3", "tot_ret_lin_5", "tot_ret_lin_7", "tot_ret_lin_10")
         , unity_coefs=T)
  , list(name="x_g110_ns_n", type="ns_n", maturity=110
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3", "tot_ret_ns_5", "tot_ret_ns_7", "tot_ret_ns_10")
         , unity_coefs=T)
  , list(name="x_g110_coup_ladder", type="coup_ladder", maturity=110
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3", "coup_inc_lin_5", "coup_inc_lin_7", "coup_inc_lin_10")
         , unity_coefs=F)
  , list(name="x_g110_shift_ladder", type="shift_ladder", maturity=110
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3", "tot_ret_shift_5", "tot_ret_shift_7", "tot_ret_shift_10")
         , unity_coefs=F)
  , list(name="x_g110_lin_ladder", type="lin_ladder", maturity=110
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3", "tot_ret_lin_5", "tot_ret_lin_7", "tot_ret_lin_10")
         , unity_coefs=F)
  , list(name="x_g110_ns_ladder", type="ns_ladder", maturity=110
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3", "tot_ret_ns_5", "tot_ret_ns_7", "tot_ret_ns_10")
         , unity_coefs=F)
)

g7_features <- sort(unique(rbindlist(lapply(g7_models, function(x) data.table(feature=x$features))))$feature)

Model evaluation - full history

evaluate <- function(model, per_in_year=12, wts=NULL) {
  if (is.null(wts))
    wts <- rep(1/length(model$y), length(model$y))
  else
    wts <- wts / sum(wts)
  
  y_hat <- model$X %*% model$coefs
  residuals <- y_hat - model$y
  return(
    c(
      R2 = caim::wtd_cor(y_hat, model$y, wts=wts) ^ 2
      # , MSE = caim::wtd_mean(residuals^2, wts=wts) # caim::wtd_mean(residuals^2, wts=wts)
      , RMSE = sqrt(caim::wtd_mean(residuals^2, wts=wts))
      # , alpha = mean(residuals) # caim::wtd_mean(residuals, wts=wts)
      # , te = sd(residuals) # caim::wtd_sd(residuals, wts=wts)
      , alpha_y = (1 + caim::wtd_mean(residuals, wts=wts)) ^ per_in_year - 1
      , te_y = caim::wtd_sd(residuals, wts=wts) * sqrt(per_in_year)
    )
  )
}

# for every combination of country and maturity
hist_eval_list <- apply(g7_assets, 1, function(asset) {
  y_data <- g7_asset_data[id==asset["id"]]
  x_data <- g7_return_data[country==asset["country"]][
    , c("year", "month") := .(
      lubridate::year(date)
      , lubridate::month(date)
    )
  ]
  # print(paste(asset["country"], asset["maturity"], nrow(y_data), nrow(x_data)))
  model_data <- y_data[
    x_data
    ,
    , on=.(year, month), nomatch=NULL
  ][
    , !c(
      "country", "i.country", "class", "maturity", "year", "month", "index_nav", "i.date"
    )
  ]

  has_invalid <- rowSums(is.na(model_data[ , ..g7_features])) > 0
  print(paste(asset["id"], "rows:", nrow(model_data), "invalid:", sum(has_invalid)))

  model_data <- model_data[!has_invalid]
  print(paste("clean rows:", nrow(model_data)))
  
  asset_models <- lapply(which(sapply(g7_models
                                      , function(x) x$maturity==as.numeric(asset["maturity"])))
                         , function(x) {
                           g7_models[[x]] 
                         })
  
  res <- lapply(asset_models, function (m) {
    print(paste(asset["id"], m$name))
    y <- model_data[ , index_ret]
    X <- cbind(intercept=1, model_data[ , m$features, with=F])
    
    Aeq <- c(0, rep(1, ncol(X) - 1))
    beq <- 1
    lb <- c(caim::NEG_INF, rep(0, ncol(X) - 1))
    wts <- caim::d_exp(caim::halflife2decay(12), nrow(X))

    fixed_coefs <- NULL
    if (m$unity_coefs)
      fixed_coefs <- c(0, rep(1/length(m$features), length(m$features)))

    lmq <- caim::lm_quad(y, X, Aeq=Aeq, beq=beq, lb=lb, wts=wts, fixed_coefs=fixed_coefs)
    # # coefs <- round(lmq$coefs, 6)
    # # print(paste(x, coefs))

    # print(lmq$coefs)
    # return(round(evaluate(lmq), 6))
    
    coefs <- round(lmq$coefs, 6)

    return(data.table(asset=asset["id"]
           , model=m$name
           , t(round(evaluate(lmq, wts=wts), 6))
           , coefs=list(coefs)
           , features=list(names(coefs))
          ))
    # return(list(name=m$name, coefs=round(lmq$coefs, 6), eval=round(evaluate(lmq), 6)))
    # return(nrow(X))
  })

  # y <- model_data[ , index_ret]
  # X <- cbind(intercept=1, model_data[ , model$features])

  return(res)

})
[1] "CA.GOVT.13 rows: 308 invalid: 3"
[1] "clean rows: 305"
[1] "CA.GOVT.13 x_g13_coup_bullet"
[1] "CA.GOVT.13 x_g13_shift_bullet"
[1] "CA.GOVT.13 x_g13_lin_bullet"
[1] "CA.GOVT.13 x_g13_ns_bullet"
[1] "CA.GOVT.13 x_g13_coup_n"
[1] "CA.GOVT.13 x_g13_shift_n"
[1] "CA.GOVT.13 x_g13_lin_n"
[1] "CA.GOVT.13 x_g13_ns_n"
[1] "CA.GOVT.13 x_g13_coup_ladder"
[1] "CA.GOVT.13 x_g13_shift_ladder"
[1] "CA.GOVT.13 x_g13_lin_ladder"
[1] "CA.GOVT.13 x_g13_ns_ladder"
[1] "CA.GOVT.15 rows: 308 invalid: 3"
[1] "clean rows: 305"
[1] "CA.GOVT.15 x_g15_coup_bullet"
[1] "CA.GOVT.15 x_g15_shift_bullet"
[1] "CA.GOVT.15 x_g15_lin_bullet"
[1] "CA.GOVT.15 x_g15_ns_bullet"
[1] "CA.GOVT.15 x_g15_coup_n"
[1] "CA.GOVT.15 x_g15_shift_n"
[1] "CA.GOVT.15 x_g15_lin_n"
[1] "CA.GOVT.15 x_g15_ns_n"
[1] "CA.GOVT.15 x_g15_coup_ladder"
[1] "CA.GOVT.15 x_g15_shift_ladder"
[1] "CA.GOVT.15 x_g15_lin_ladder"
[1] "CA.GOVT.15 x_g15_ns_ladder"
[1] "CA.GOVT.110 rows: 308 invalid: 3"
[1] "clean rows: 305"
[1] "CA.GOVT.110 x_g110_coup_bullet"
[1] "CA.GOVT.110 x_g110_shift_bullet"
[1] "CA.GOVT.110 x_g110_lin_bullet"
[1] "CA.GOVT.110 x_g110_ns_bullet"
[1] "CA.GOVT.110 x_g110_coup_n"
[1] "CA.GOVT.110 x_g110_shift_n"
[1] "CA.GOVT.110 x_g110_lin_n"
[1] "CA.GOVT.110 x_g110_ns_n"
[1] "CA.GOVT.110 x_g110_coup_ladder"
[1] "CA.GOVT.110 x_g110_shift_ladder"
[1] "CA.GOVT.110 x_g110_lin_ladder"
[1] "CA.GOVT.110 x_g110_ns_ladder"
[1] "DE.GOVT.13 rows: 307 invalid: 3"
[1] "clean rows: 304"
[1] "DE.GOVT.13 x_g13_coup_bullet"
[1] "DE.GOVT.13 x_g13_shift_bullet"
[1] "DE.GOVT.13 x_g13_lin_bullet"
[1] "DE.GOVT.13 x_g13_ns_bullet"
[1] "DE.GOVT.13 x_g13_coup_n"
[1] "DE.GOVT.13 x_g13_shift_n"
[1] "DE.GOVT.13 x_g13_lin_n"
[1] "DE.GOVT.13 x_g13_ns_n"
[1] "DE.GOVT.13 x_g13_coup_ladder"
[1] "DE.GOVT.13 x_g13_shift_ladder"
[1] "DE.GOVT.13 x_g13_lin_ladder"
[1] "DE.GOVT.13 x_g13_ns_ladder"
[1] "DE.GOVT.15 rows: 307 invalid: 3"
[1] "clean rows: 304"
[1] "DE.GOVT.15 x_g15_coup_bullet"
[1] "DE.GOVT.15 x_g15_shift_bullet"
[1] "DE.GOVT.15 x_g15_lin_bullet"
[1] "DE.GOVT.15 x_g15_ns_bullet"
[1] "DE.GOVT.15 x_g15_coup_n"
[1] "DE.GOVT.15 x_g15_shift_n"
[1] "DE.GOVT.15 x_g15_lin_n"
[1] "DE.GOVT.15 x_g15_ns_n"
[1] "DE.GOVT.15 x_g15_coup_ladder"
[1] "DE.GOVT.15 x_g15_shift_ladder"
[1] "DE.GOVT.15 x_g15_lin_ladder"
[1] "DE.GOVT.15 x_g15_ns_ladder"
[1] "DE.GOVT.110 rows: 307 invalid: 3"
[1] "clean rows: 304"
[1] "DE.GOVT.110 x_g110_coup_bullet"
[1] "DE.GOVT.110 x_g110_shift_bullet"
[1] "DE.GOVT.110 x_g110_lin_bullet"
[1] "DE.GOVT.110 x_g110_ns_bullet"
[1] "DE.GOVT.110 x_g110_coup_n"
[1] "DE.GOVT.110 x_g110_shift_n"
[1] "DE.GOVT.110 x_g110_lin_n"
[1] "DE.GOVT.110 x_g110_ns_n"
[1] "DE.GOVT.110 x_g110_coup_ladder"
[1] "DE.GOVT.110 x_g110_shift_ladder"
[1] "DE.GOVT.110 x_g110_lin_ladder"
[1] "DE.GOVT.110 x_g110_ns_ladder"
[1] "FR.GOVT.13 rows: 306 invalid: 3"
[1] "clean rows: 303"
[1] "FR.GOVT.13 x_g13_coup_bullet"
[1] "FR.GOVT.13 x_g13_shift_bullet"
[1] "FR.GOVT.13 x_g13_lin_bullet"
[1] "FR.GOVT.13 x_g13_ns_bullet"
[1] "FR.GOVT.13 x_g13_coup_n"
[1] "FR.GOVT.13 x_g13_shift_n"
[1] "FR.GOVT.13 x_g13_lin_n"
[1] "FR.GOVT.13 x_g13_ns_n"
[1] "FR.GOVT.13 x_g13_coup_ladder"
[1] "FR.GOVT.13 x_g13_shift_ladder"
[1] "FR.GOVT.13 x_g13_lin_ladder"
[1] "FR.GOVT.13 x_g13_ns_ladder"
[1] "FR.GOVT.15 rows: 306 invalid: 3"
[1] "clean rows: 303"
[1] "FR.GOVT.15 x_g15_coup_bullet"
[1] "FR.GOVT.15 x_g15_shift_bullet"
[1] "FR.GOVT.15 x_g15_lin_bullet"
[1] "FR.GOVT.15 x_g15_ns_bullet"
[1] "FR.GOVT.15 x_g15_coup_n"
[1] "FR.GOVT.15 x_g15_shift_n"
[1] "FR.GOVT.15 x_g15_lin_n"
[1] "FR.GOVT.15 x_g15_ns_n"
[1] "FR.GOVT.15 x_g15_coup_ladder"
[1] "FR.GOVT.15 x_g15_shift_ladder"
[1] "FR.GOVT.15 x_g15_lin_ladder"
[1] "FR.GOVT.15 x_g15_ns_ladder"
[1] "FR.GOVT.110 rows: 306 invalid: 3"
[1] "clean rows: 303"
[1] "FR.GOVT.110 x_g110_coup_bullet"
[1] "FR.GOVT.110 x_g110_shift_bullet"
[1] "FR.GOVT.110 x_g110_lin_bullet"
[1] "FR.GOVT.110 x_g110_ns_bullet"
[1] "FR.GOVT.110 x_g110_coup_n"
[1] "FR.GOVT.110 x_g110_shift_n"
[1] "FR.GOVT.110 x_g110_lin_n"
[1] "FR.GOVT.110 x_g110_ns_n"
[1] "FR.GOVT.110 x_g110_coup_ladder"
[1] "FR.GOVT.110 x_g110_shift_ladder"
[1] "FR.GOVT.110 x_g110_lin_ladder"
[1] "FR.GOVT.110 x_g110_ns_ladder"
[1] "IT.GOVT.13 rows: 307 invalid: 17"
[1] "clean rows: 290"
[1] "IT.GOVT.13 x_g13_coup_bullet"
[1] "IT.GOVT.13 x_g13_shift_bullet"
[1] "IT.GOVT.13 x_g13_lin_bullet"
[1] "IT.GOVT.13 x_g13_ns_bullet"
[1] "IT.GOVT.13 x_g13_coup_n"
[1] "IT.GOVT.13 x_g13_shift_n"
[1] "IT.GOVT.13 x_g13_lin_n"
[1] "IT.GOVT.13 x_g13_ns_n"
[1] "IT.GOVT.13 x_g13_coup_ladder"
[1] "IT.GOVT.13 x_g13_shift_ladder"
[1] "IT.GOVT.13 x_g13_lin_ladder"
[1] "IT.GOVT.13 x_g13_ns_ladder"
[1] "IT.GOVT.15 rows: 307 invalid: 17"
[1] "clean rows: 290"
[1] "IT.GOVT.15 x_g15_coup_bullet"
[1] "IT.GOVT.15 x_g15_shift_bullet"
[1] "IT.GOVT.15 x_g15_lin_bullet"
[1] "IT.GOVT.15 x_g15_ns_bullet"
[1] "IT.GOVT.15 x_g15_coup_n"
[1] "IT.GOVT.15 x_g15_shift_n"
[1] "IT.GOVT.15 x_g15_lin_n"
[1] "IT.GOVT.15 x_g15_ns_n"
[1] "IT.GOVT.15 x_g15_coup_ladder"
[1] "IT.GOVT.15 x_g15_shift_ladder"
[1] "IT.GOVT.15 x_g15_lin_ladder"
[1] "IT.GOVT.15 x_g15_ns_ladder"
[1] "IT.GOVT.110 rows: 307 invalid: 17"
[1] "clean rows: 290"
[1] "IT.GOVT.110 x_g110_coup_bullet"
[1] "IT.GOVT.110 x_g110_shift_bullet"
[1] "IT.GOVT.110 x_g110_lin_bullet"
[1] "IT.GOVT.110 x_g110_ns_bullet"
[1] "IT.GOVT.110 x_g110_coup_n"
[1] "IT.GOVT.110 x_g110_shift_n"
[1] "IT.GOVT.110 x_g110_lin_n"
[1] "IT.GOVT.110 x_g110_ns_n"
[1] "IT.GOVT.110 x_g110_coup_ladder"
[1] "IT.GOVT.110 x_g110_shift_ladder"
[1] "IT.GOVT.110 x_g110_lin_ladder"
[1] "IT.GOVT.110 x_g110_ns_ladder"
[1] "JP.GOVT.13 rows: 304 invalid: 5"
[1] "clean rows: 299"
[1] "JP.GOVT.13 x_g13_coup_bullet"
[1] "JP.GOVT.13 x_g13_shift_bullet"
[1] "JP.GOVT.13 x_g13_lin_bullet"
[1] "JP.GOVT.13 x_g13_ns_bullet"
[1] "JP.GOVT.13 x_g13_coup_n"
[1] "JP.GOVT.13 x_g13_shift_n"
[1] "JP.GOVT.13 x_g13_lin_n"
[1] "JP.GOVT.13 x_g13_ns_n"
[1] "JP.GOVT.13 x_g13_coup_ladder"
[1] "JP.GOVT.13 x_g13_shift_ladder"
[1] "JP.GOVT.13 x_g13_lin_ladder"
[1] "JP.GOVT.13 x_g13_ns_ladder"
[1] "JP.GOVT.15 rows: 304 invalid: 5"
[1] "clean rows: 299"
[1] "JP.GOVT.15 x_g15_coup_bullet"
[1] "JP.GOVT.15 x_g15_shift_bullet"
[1] "JP.GOVT.15 x_g15_lin_bullet"
[1] "JP.GOVT.15 x_g15_ns_bullet"
[1] "JP.GOVT.15 x_g15_coup_n"
[1] "JP.GOVT.15 x_g15_shift_n"
[1] "JP.GOVT.15 x_g15_lin_n"
[1] "JP.GOVT.15 x_g15_ns_n"
[1] "JP.GOVT.15 x_g15_coup_ladder"
[1] "JP.GOVT.15 x_g15_shift_ladder"
[1] "JP.GOVT.15 x_g15_lin_ladder"
[1] "JP.GOVT.15 x_g15_ns_ladder"
[1] "JP.GOVT.110 rows: 304 invalid: 5"
[1] "clean rows: 299"
[1] "JP.GOVT.110 x_g110_coup_bullet"
[1] "JP.GOVT.110 x_g110_shift_bullet"
[1] "JP.GOVT.110 x_g110_lin_bullet"
[1] "JP.GOVT.110 x_g110_ns_bullet"
[1] "JP.GOVT.110 x_g110_coup_n"
[1] "JP.GOVT.110 x_g110_shift_n"
[1] "JP.GOVT.110 x_g110_lin_n"
[1] "JP.GOVT.110 x_g110_ns_n"
[1] "JP.GOVT.110 x_g110_coup_ladder"
[1] "JP.GOVT.110 x_g110_shift_ladder"
[1] "JP.GOVT.110 x_g110_lin_ladder"
[1] "JP.GOVT.110 x_g110_ns_ladder"
[1] "UK.GOVT.13 rows: 306 invalid: 6"
[1] "clean rows: 300"
[1] "UK.GOVT.13 x_g13_coup_bullet"
[1] "UK.GOVT.13 x_g13_shift_bullet"
[1] "UK.GOVT.13 x_g13_lin_bullet"
[1] "UK.GOVT.13 x_g13_ns_bullet"
[1] "UK.GOVT.13 x_g13_coup_n"
[1] "UK.GOVT.13 x_g13_shift_n"
[1] "UK.GOVT.13 x_g13_lin_n"
[1] "UK.GOVT.13 x_g13_ns_n"
[1] "UK.GOVT.13 x_g13_coup_ladder"
[1] "UK.GOVT.13 x_g13_shift_ladder"
[1] "UK.GOVT.13 x_g13_lin_ladder"
[1] "UK.GOVT.13 x_g13_ns_ladder"
[1] "UK.GOVT.15 rows: 306 invalid: 6"
[1] "clean rows: 300"
[1] "UK.GOVT.15 x_g15_coup_bullet"
[1] "UK.GOVT.15 x_g15_shift_bullet"
[1] "UK.GOVT.15 x_g15_lin_bullet"
[1] "UK.GOVT.15 x_g15_ns_bullet"
[1] "UK.GOVT.15 x_g15_coup_n"
[1] "UK.GOVT.15 x_g15_shift_n"
[1] "UK.GOVT.15 x_g15_lin_n"
[1] "UK.GOVT.15 x_g15_ns_n"
[1] "UK.GOVT.15 x_g15_coup_ladder"
[1] "UK.GOVT.15 x_g15_shift_ladder"
[1] "UK.GOVT.15 x_g15_lin_ladder"
[1] "UK.GOVT.15 x_g15_ns_ladder"
[1] "UK.GOVT.110 rows: 306 invalid: 6"
[1] "clean rows: 300"
[1] "UK.GOVT.110 x_g110_coup_bullet"
[1] "UK.GOVT.110 x_g110_shift_bullet"
[1] "UK.GOVT.110 x_g110_lin_bullet"
[1] "UK.GOVT.110 x_g110_ns_bullet"
[1] "UK.GOVT.110 x_g110_coup_n"
[1] "UK.GOVT.110 x_g110_shift_n"
[1] "UK.GOVT.110 x_g110_lin_n"
[1] "UK.GOVT.110 x_g110_ns_n"
[1] "UK.GOVT.110 x_g110_coup_ladder"
[1] "UK.GOVT.110 x_g110_shift_ladder"
[1] "UK.GOVT.110 x_g110_lin_ladder"
[1] "UK.GOVT.110 x_g110_ns_ladder"
[1] "US.GOVT.13 rows: 308 invalid: 1"
[1] "clean rows: 307"
[1] "US.GOVT.13 x_g13_coup_bullet"
[1] "US.GOVT.13 x_g13_shift_bullet"
[1] "US.GOVT.13 x_g13_lin_bullet"
[1] "US.GOVT.13 x_g13_ns_bullet"
[1] "US.GOVT.13 x_g13_coup_n"
[1] "US.GOVT.13 x_g13_shift_n"
[1] "US.GOVT.13 x_g13_lin_n"
[1] "US.GOVT.13 x_g13_ns_n"
[1] "US.GOVT.13 x_g13_coup_ladder"
[1] "US.GOVT.13 x_g13_shift_ladder"
[1] "US.GOVT.13 x_g13_lin_ladder"
[1] "US.GOVT.13 x_g13_ns_ladder"
[1] "US.GOVT.15 rows: 308 invalid: 1"
[1] "clean rows: 307"
[1] "US.GOVT.15 x_g15_coup_bullet"
[1] "US.GOVT.15 x_g15_shift_bullet"
[1] "US.GOVT.15 x_g15_lin_bullet"
[1] "US.GOVT.15 x_g15_ns_bullet"
[1] "US.GOVT.15 x_g15_coup_n"
[1] "US.GOVT.15 x_g15_shift_n"
[1] "US.GOVT.15 x_g15_lin_n"
[1] "US.GOVT.15 x_g15_ns_n"
[1] "US.GOVT.15 x_g15_coup_ladder"
[1] "US.GOVT.15 x_g15_shift_ladder"
[1] "US.GOVT.15 x_g15_lin_ladder"
[1] "US.GOVT.15 x_g15_ns_ladder"
[1] "US.GOVT.110 rows: 308 invalid: 1"
[1] "clean rows: 307"
[1] "US.GOVT.110 x_g110_coup_bullet"
[1] "US.GOVT.110 x_g110_shift_bullet"
[1] "US.GOVT.110 x_g110_lin_bullet"
[1] "US.GOVT.110 x_g110_ns_bullet"
[1] "US.GOVT.110 x_g110_coup_n"
[1] "US.GOVT.110 x_g110_shift_n"
[1] "US.GOVT.110 x_g110_lin_n"
[1] "US.GOVT.110 x_g110_ns_n"
[1] "US.GOVT.110 x_g110_coup_ladder"
[1] "US.GOVT.110 x_g110_shift_ladder"
[1] "US.GOVT.110 x_g110_lin_ladder"
[1] "US.GOVT.110 x_g110_ns_ladder"
# combine eval_list, which is a [[numassets]][[nummodels]] list
hist_eval <- rbindlist(lapply(hist_eval_list, rbindlist))
hist_eval[]

K-fold cross validation

k_fold_eval <- rbindlist(apply(g7_assets, 1, function(asset) {
  y_data <- g7_asset_data[id==asset["id"]]
  x_data <- g7_return_data[country==asset["country"]][
    , c("year", "month") := .(
      lubridate::year(date)
      , lubridate::month(date)
    )
  ]
  # print(paste(asset["country"], asset["maturity"], nrow(y_data), nrow(x_data)))
  model_data <- y_data[
    x_data
    ,
    , on=.(year, month), nomatch=NULL
  ][
    , !c(
      "country", "i.country", "class", "maturity", "year", "month", "index_nav", "i.date"
    )
  ]

  has_invalid <- rowSums(is.na(model_data[ , ..g7_features])) > 0
  # print(paste(asset["id"], "rows:", nrow(model_data), "invalid:", sum(has_invalid)))

  model_data <- model_data[!has_invalid]
  # print(paste("clean rows:", nrow(model_data)))
  
  k_ix <- caim::k_fold_ix(nrow(model_data), NUM_K_FOLDS)
  
  asset_models <- lapply(which(sapply(g7_models
                                      , function(x) x$maturity==as.numeric(asset["maturity"])))
                         , function(x) {
                           g7_models[[x]] 
                         })
  
  model_res <- lapply(asset_models, function (m) {
    print(paste(asset["id"], m$name))
    y <- model_data[ , index_ret]
    X <- cbind(intercept=1, model_data[ , m$features, with=F])
    
    k_res <- sapply(seq(1, NUM_K_FOLDS), function(fold) {
      train_ix <- k_ix[k != fold, ix]
      test_ix <- k_ix[k == fold, ix]
      
      train_y <- y[train_ix]
      train_X <- X[train_ix]

      Aeq <- c(0, rep(1, ncol(train_X) - 1))
      beq <- 1
      lb <- c(caim::NEG_INF, rep(0, ncol(train_X) - 1))
      wts <- caim::d_exp(caim::halflife2decay(12), nrow(train_X))
  
      fixed_coefs <- NULL
      if (m$unity_coefs)
        fixed_coefs <- c(0, rep(1/length(m$features), length(m$features)))
      
      lmq <- caim::lm_quad(train_y, train_X
                           , Aeq=Aeq
                           , beq=beq
                           , lb=lb
                           , wts=wts
                           , fixed_coefs=fixed_coefs
                           )
      
      test_y <- y[test_ix]
      test_X <- X[test_ix]
      
      # print(test_X)
      test_y_hat <- as.matrix(test_X) %*% lmq$coefs
      residuals <- test_y_hat - test_y
      R2 <- cor(test_y, test_y_hat) ^ 2
      RMSE <- sqrt(mean(residuals^2))
      alpha_y <- (1 + mean(residuals)) ^ 12 - 1
      te_y <- sd(residuals) * sqrt(12)

      return(round(c(k=fold, R2=R2, RMSE=RMSE, alpha_y=alpha_y, te_y=te_y), 6))

    })
    
    return(data.table(asset=asset["id"], model=m$name, type=m$type, t(k_res)))

  })

  return(rbindlist(model_res))

}))[
  , .(
    R2_mean = mean(R2)
    , RMSE_mean = mean(RMSE)
    , alpha_y_mean = mean(alpha_y)
    , te_y_mean = mean(te_y)
    , R2_sd = sd(R2)
    , RMSE_sd = sd(RMSE)
    , alpha_y_sd = sd(alpha_y)
    , te_y_sd = sd(te_y)
    , score = mean(RMSE) * sd(RMSE)
  )
  , by=.(asset, model, type)
  ][
    g7_assets, on=c(asset="id")
  ][
    , .SD
    , key=.(country, maturity, score)
  ]
[1] "CA.GOVT.13 x_g13_coup_bullet"
[1] "CA.GOVT.13 x_g13_shift_bullet"
[1] "CA.GOVT.13 x_g13_lin_bullet"
[1] "CA.GOVT.13 x_g13_ns_bullet"
[1] "CA.GOVT.13 x_g13_coup_n"
[1] "CA.GOVT.13 x_g13_shift_n"
[1] "CA.GOVT.13 x_g13_lin_n"
[1] "CA.GOVT.13 x_g13_ns_n"
[1] "CA.GOVT.13 x_g13_coup_ladder"
[1] "CA.GOVT.13 x_g13_shift_ladder"
[1] "CA.GOVT.13 x_g13_lin_ladder"
[1] "CA.GOVT.13 x_g13_ns_ladder"
[1] "CA.GOVT.15 x_g15_coup_bullet"
[1] "CA.GOVT.15 x_g15_shift_bullet"
[1] "CA.GOVT.15 x_g15_lin_bullet"
[1] "CA.GOVT.15 x_g15_ns_bullet"
[1] "CA.GOVT.15 x_g15_coup_n"
[1] "CA.GOVT.15 x_g15_shift_n"
[1] "CA.GOVT.15 x_g15_lin_n"
[1] "CA.GOVT.15 x_g15_ns_n"
[1] "CA.GOVT.15 x_g15_coup_ladder"
[1] "CA.GOVT.15 x_g15_shift_ladder"
[1] "CA.GOVT.15 x_g15_lin_ladder"
[1] "CA.GOVT.15 x_g15_ns_ladder"
[1] "CA.GOVT.110 x_g110_coup_bullet"
[1] "CA.GOVT.110 x_g110_shift_bullet"
[1] "CA.GOVT.110 x_g110_lin_bullet"
[1] "CA.GOVT.110 x_g110_ns_bullet"
[1] "CA.GOVT.110 x_g110_coup_n"
[1] "CA.GOVT.110 x_g110_shift_n"
[1] "CA.GOVT.110 x_g110_lin_n"
[1] "CA.GOVT.110 x_g110_ns_n"
[1] "CA.GOVT.110 x_g110_coup_ladder"
[1] "CA.GOVT.110 x_g110_shift_ladder"
[1] "CA.GOVT.110 x_g110_lin_ladder"
[1] "CA.GOVT.110 x_g110_ns_ladder"
[1] "DE.GOVT.13 x_g13_coup_bullet"
[1] "DE.GOVT.13 x_g13_shift_bullet"
[1] "DE.GOVT.13 x_g13_lin_bullet"
[1] "DE.GOVT.13 x_g13_ns_bullet"
[1] "DE.GOVT.13 x_g13_coup_n"
[1] "DE.GOVT.13 x_g13_shift_n"
[1] "DE.GOVT.13 x_g13_lin_n"
[1] "DE.GOVT.13 x_g13_ns_n"
[1] "DE.GOVT.13 x_g13_coup_ladder"
[1] "DE.GOVT.13 x_g13_shift_ladder"
[1] "DE.GOVT.13 x_g13_lin_ladder"
[1] "DE.GOVT.13 x_g13_ns_ladder"
[1] "DE.GOVT.15 x_g15_coup_bullet"
[1] "DE.GOVT.15 x_g15_shift_bullet"
[1] "DE.GOVT.15 x_g15_lin_bullet"
[1] "DE.GOVT.15 x_g15_ns_bullet"
[1] "DE.GOVT.15 x_g15_coup_n"
[1] "DE.GOVT.15 x_g15_shift_n"
[1] "DE.GOVT.15 x_g15_lin_n"
[1] "DE.GOVT.15 x_g15_ns_n"
[1] "DE.GOVT.15 x_g15_coup_ladder"
[1] "DE.GOVT.15 x_g15_shift_ladder"
[1] "DE.GOVT.15 x_g15_lin_ladder"
[1] "DE.GOVT.15 x_g15_ns_ladder"
[1] "DE.GOVT.110 x_g110_coup_bullet"
[1] "DE.GOVT.110 x_g110_shift_bullet"
[1] "DE.GOVT.110 x_g110_lin_bullet"
[1] "DE.GOVT.110 x_g110_ns_bullet"
[1] "DE.GOVT.110 x_g110_coup_n"
[1] "DE.GOVT.110 x_g110_shift_n"
[1] "DE.GOVT.110 x_g110_lin_n"
[1] "DE.GOVT.110 x_g110_ns_n"
[1] "DE.GOVT.110 x_g110_coup_ladder"
[1] "DE.GOVT.110 x_g110_shift_ladder"
[1] "DE.GOVT.110 x_g110_lin_ladder"
[1] "DE.GOVT.110 x_g110_ns_ladder"
[1] "FR.GOVT.13 x_g13_coup_bullet"
[1] "FR.GOVT.13 x_g13_shift_bullet"
[1] "FR.GOVT.13 x_g13_lin_bullet"
[1] "FR.GOVT.13 x_g13_ns_bullet"
[1] "FR.GOVT.13 x_g13_coup_n"
[1] "FR.GOVT.13 x_g13_shift_n"
[1] "FR.GOVT.13 x_g13_lin_n"
[1] "FR.GOVT.13 x_g13_ns_n"
[1] "FR.GOVT.13 x_g13_coup_ladder"
[1] "FR.GOVT.13 x_g13_shift_ladder"
[1] "FR.GOVT.13 x_g13_lin_ladder"
[1] "FR.GOVT.13 x_g13_ns_ladder"
[1] "FR.GOVT.15 x_g15_coup_bullet"
[1] "FR.GOVT.15 x_g15_shift_bullet"
[1] "FR.GOVT.15 x_g15_lin_bullet"
[1] "FR.GOVT.15 x_g15_ns_bullet"
[1] "FR.GOVT.15 x_g15_coup_n"
[1] "FR.GOVT.15 x_g15_shift_n"
[1] "FR.GOVT.15 x_g15_lin_n"
[1] "FR.GOVT.15 x_g15_ns_n"
[1] "FR.GOVT.15 x_g15_coup_ladder"
[1] "FR.GOVT.15 x_g15_shift_ladder"
[1] "FR.GOVT.15 x_g15_lin_ladder"
[1] "FR.GOVT.15 x_g15_ns_ladder"
[1] "FR.GOVT.110 x_g110_coup_bullet"
[1] "FR.GOVT.110 x_g110_shift_bullet"
[1] "FR.GOVT.110 x_g110_lin_bullet"
[1] "FR.GOVT.110 x_g110_ns_bullet"
[1] "FR.GOVT.110 x_g110_coup_n"
[1] "FR.GOVT.110 x_g110_shift_n"
[1] "FR.GOVT.110 x_g110_lin_n"
[1] "FR.GOVT.110 x_g110_ns_n"
[1] "FR.GOVT.110 x_g110_coup_ladder"
[1] "FR.GOVT.110 x_g110_shift_ladder"
[1] "FR.GOVT.110 x_g110_lin_ladder"
[1] "FR.GOVT.110 x_g110_ns_ladder"
[1] "IT.GOVT.13 x_g13_coup_bullet"
[1] "IT.GOVT.13 x_g13_shift_bullet"
[1] "IT.GOVT.13 x_g13_lin_bullet"
[1] "IT.GOVT.13 x_g13_ns_bullet"
[1] "IT.GOVT.13 x_g13_coup_n"
[1] "IT.GOVT.13 x_g13_shift_n"
[1] "IT.GOVT.13 x_g13_lin_n"
[1] "IT.GOVT.13 x_g13_ns_n"
[1] "IT.GOVT.13 x_g13_coup_ladder"
[1] "IT.GOVT.13 x_g13_shift_ladder"
[1] "IT.GOVT.13 x_g13_lin_ladder"
[1] "IT.GOVT.13 x_g13_ns_ladder"
[1] "IT.GOVT.15 x_g15_coup_bullet"
[1] "IT.GOVT.15 x_g15_shift_bullet"
[1] "IT.GOVT.15 x_g15_lin_bullet"
[1] "IT.GOVT.15 x_g15_ns_bullet"
[1] "IT.GOVT.15 x_g15_coup_n"
[1] "IT.GOVT.15 x_g15_shift_n"
[1] "IT.GOVT.15 x_g15_lin_n"
[1] "IT.GOVT.15 x_g15_ns_n"
[1] "IT.GOVT.15 x_g15_coup_ladder"
[1] "IT.GOVT.15 x_g15_shift_ladder"
[1] "IT.GOVT.15 x_g15_lin_ladder"
[1] "IT.GOVT.15 x_g15_ns_ladder"
[1] "IT.GOVT.110 x_g110_coup_bullet"
[1] "IT.GOVT.110 x_g110_shift_bullet"
[1] "IT.GOVT.110 x_g110_lin_bullet"
[1] "IT.GOVT.110 x_g110_ns_bullet"
[1] "IT.GOVT.110 x_g110_coup_n"
[1] "IT.GOVT.110 x_g110_shift_n"
[1] "IT.GOVT.110 x_g110_lin_n"
[1] "IT.GOVT.110 x_g110_ns_n"
[1] "IT.GOVT.110 x_g110_coup_ladder"
[1] "IT.GOVT.110 x_g110_shift_ladder"
[1] "IT.GOVT.110 x_g110_lin_ladder"
[1] "IT.GOVT.110 x_g110_ns_ladder"
[1] "JP.GOVT.13 x_g13_coup_bullet"
[1] "JP.GOVT.13 x_g13_shift_bullet"
[1] "JP.GOVT.13 x_g13_lin_bullet"
[1] "JP.GOVT.13 x_g13_ns_bullet"
[1] "JP.GOVT.13 x_g13_coup_n"
[1] "JP.GOVT.13 x_g13_shift_n"
[1] "JP.GOVT.13 x_g13_lin_n"
[1] "JP.GOVT.13 x_g13_ns_n"
[1] "JP.GOVT.13 x_g13_coup_ladder"
[1] "JP.GOVT.13 x_g13_shift_ladder"
[1] "JP.GOVT.13 x_g13_lin_ladder"
[1] "JP.GOVT.13 x_g13_ns_ladder"
[1] "JP.GOVT.15 x_g15_coup_bullet"
[1] "JP.GOVT.15 x_g15_shift_bullet"
[1] "JP.GOVT.15 x_g15_lin_bullet"
[1] "JP.GOVT.15 x_g15_ns_bullet"
[1] "JP.GOVT.15 x_g15_coup_n"
[1] "JP.GOVT.15 x_g15_shift_n"
[1] "JP.GOVT.15 x_g15_lin_n"
[1] "JP.GOVT.15 x_g15_ns_n"
[1] "JP.GOVT.15 x_g15_coup_ladder"
[1] "JP.GOVT.15 x_g15_shift_ladder"
[1] "JP.GOVT.15 x_g15_lin_ladder"
[1] "JP.GOVT.15 x_g15_ns_ladder"
[1] "JP.GOVT.110 x_g110_coup_bullet"
[1] "JP.GOVT.110 x_g110_shift_bullet"
[1] "JP.GOVT.110 x_g110_lin_bullet"
[1] "JP.GOVT.110 x_g110_ns_bullet"
[1] "JP.GOVT.110 x_g110_coup_n"
[1] "JP.GOVT.110 x_g110_shift_n"
[1] "JP.GOVT.110 x_g110_lin_n"
[1] "JP.GOVT.110 x_g110_ns_n"
[1] "JP.GOVT.110 x_g110_coup_ladder"
[1] "JP.GOVT.110 x_g110_shift_ladder"
[1] "JP.GOVT.110 x_g110_lin_ladder"
[1] "JP.GOVT.110 x_g110_ns_ladder"
[1] "UK.GOVT.13 x_g13_coup_bullet"
[1] "UK.GOVT.13 x_g13_shift_bullet"
[1] "UK.GOVT.13 x_g13_lin_bullet"
[1] "UK.GOVT.13 x_g13_ns_bullet"
[1] "UK.GOVT.13 x_g13_coup_n"
[1] "UK.GOVT.13 x_g13_shift_n"
[1] "UK.GOVT.13 x_g13_lin_n"
[1] "UK.GOVT.13 x_g13_ns_n"
[1] "UK.GOVT.13 x_g13_coup_ladder"
[1] "UK.GOVT.13 x_g13_shift_ladder"
[1] "UK.GOVT.13 x_g13_lin_ladder"
[1] "UK.GOVT.13 x_g13_ns_ladder"
[1] "UK.GOVT.15 x_g15_coup_bullet"
[1] "UK.GOVT.15 x_g15_shift_bullet"
[1] "UK.GOVT.15 x_g15_lin_bullet"
[1] "UK.GOVT.15 x_g15_ns_bullet"
[1] "UK.GOVT.15 x_g15_coup_n"
[1] "UK.GOVT.15 x_g15_shift_n"
[1] "UK.GOVT.15 x_g15_lin_n"
[1] "UK.GOVT.15 x_g15_ns_n"
[1] "UK.GOVT.15 x_g15_coup_ladder"
[1] "UK.GOVT.15 x_g15_shift_ladder"
[1] "UK.GOVT.15 x_g15_lin_ladder"
[1] "UK.GOVT.15 x_g15_ns_ladder"
[1] "UK.GOVT.110 x_g110_coup_bullet"
[1] "UK.GOVT.110 x_g110_shift_bullet"
[1] "UK.GOVT.110 x_g110_lin_bullet"
[1] "UK.GOVT.110 x_g110_ns_bullet"
[1] "UK.GOVT.110 x_g110_coup_n"
[1] "UK.GOVT.110 x_g110_shift_n"
[1] "UK.GOVT.110 x_g110_lin_n"
[1] "UK.GOVT.110 x_g110_ns_n"
[1] "UK.GOVT.110 x_g110_coup_ladder"
[1] "UK.GOVT.110 x_g110_shift_ladder"
[1] "UK.GOVT.110 x_g110_lin_ladder"
[1] "UK.GOVT.110 x_g110_ns_ladder"
[1] "US.GOVT.13 x_g13_coup_bullet"
[1] "US.GOVT.13 x_g13_shift_bullet"
[1] "US.GOVT.13 x_g13_lin_bullet"
[1] "US.GOVT.13 x_g13_ns_bullet"
[1] "US.GOVT.13 x_g13_coup_n"
[1] "US.GOVT.13 x_g13_shift_n"
[1] "US.GOVT.13 x_g13_lin_n"
[1] "US.GOVT.13 x_g13_ns_n"
[1] "US.GOVT.13 x_g13_coup_ladder"
[1] "US.GOVT.13 x_g13_shift_ladder"
[1] "US.GOVT.13 x_g13_lin_ladder"
[1] "US.GOVT.13 x_g13_ns_ladder"
[1] "US.GOVT.15 x_g15_coup_bullet"
[1] "US.GOVT.15 x_g15_shift_bullet"
[1] "US.GOVT.15 x_g15_lin_bullet"
[1] "US.GOVT.15 x_g15_ns_bullet"
[1] "US.GOVT.15 x_g15_coup_n"
[1] "US.GOVT.15 x_g15_shift_n"
[1] "US.GOVT.15 x_g15_lin_n"
[1] "US.GOVT.15 x_g15_ns_n"
[1] "US.GOVT.15 x_g15_coup_ladder"
[1] "US.GOVT.15 x_g15_shift_ladder"
[1] "US.GOVT.15 x_g15_lin_ladder"
[1] "US.GOVT.15 x_g15_ns_ladder"
[1] "US.GOVT.110 x_g110_coup_bullet"
[1] "US.GOVT.110 x_g110_shift_bullet"
[1] "US.GOVT.110 x_g110_lin_bullet"
[1] "US.GOVT.110 x_g110_ns_bullet"
[1] "US.GOVT.110 x_g110_coup_n"
[1] "US.GOVT.110 x_g110_shift_n"
[1] "US.GOVT.110 x_g110_lin_n"
[1] "US.GOVT.110 x_g110_ns_n"
[1] "US.GOVT.110 x_g110_coup_ladder"
[1] "US.GOVT.110 x_g110_shift_ladder"
[1] "US.GOVT.110 x_g110_lin_ladder"
[1] "US.GOVT.110 x_g110_ns_ladder"
k_fold_best_models <- k_fold_eval[
  , head(.SD, 3)
  , by=.(country, maturity, asset)
  ]

best_model_freq <- sort(table(k_fold_best_models$type), decreasing=T)
best_model_freq

       lin_n   lin_ladder shift_ladder    ns_ladder 
          15           13           13            8 
        ns_n      shift_n   lin_bullet    ns_bullet 
           5            4            3            1 
shift_bullet 
           1 

Old appendix: constrained linear least squares = quadratic programming

# t(sapply(names(g13_models), function(x) {
#   model <- g13_models[[x]]
#   y <- model_data[ , index_ret]
#   X <- cbind(intercept=1, model_data[ , model$features, with=F])
# 
#   Aeq <- c(0, rep(1, ncol(X) - 1))
#   beq <- 1
#   lb <- c(caim::NEG_INF, rep(0, ncol(X) - 1))
#   wts <- caim::d_exp(caim::halflife2decay(12), nrow(X))
#   
#   fixed_coefs <- NULL
#   if (model$unity_coefs)
#     fixed_coefs <- c(0, rep(1/length(model$features), length(model$features)))
# 
#   lmq <- caim::lm_quad(y, X, Aeq=Aeq, beq=beq, lb=lb, wts=wts, fixed_coefs=fixed_coefs)
#   # coefs <- round(lmq$coefs, 6)
#   # print(paste(x, coefs))
#   
#   print(lmq$coefs)
#   return(round(evaluate(lmq), 6))
#   # return(as.matrix(c(model=x, t(round(evaluate(lmq), 6)))))
# }))
LS0tDQp0aXRsZTogIkc3IEdvdmVybm1lbnQgWWllbGRzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMjIEluaXRpYWxpemUgZW52aXJvbm1lbnQNCmBgYHtyfQ0KbGlicmFyeShjYWltKQ0KY2FpbTo6Y2xyKCkNCmBgYA0KDQojIyMgU2V0dXAgcHJlZmVyZW5jZXMNCmBgYHtyfQ0KIyBtb3N0IGltcG9ydGFudCBtYXR1cml0aWVzDQptYXR1cml0aWVzX2EgPSBjKC4yNSwgMiwgNSwgMTApDQojIGludGVyZXN0aW5nIG1hdHVyaXRpZXMsIGxldmVsIGINCm1hdHVyaXRpZXNfYiA9IGMoLjA4MzMsIDEsIDMwKQ0KIyBhbGwgb3RoZXIgbWF0dXJpdGllcyB3aWxsIGJlIGxldmVsIGMNCg0KIyByZWxhdGl2ZSB3ZWlnaHRpbmdzIGZvciBlYWNoIG1hdHVyaXR5IGxldmVsDQojIHRoZXNlIHdpbGwgZm9ybSBhIGRlbnNpdHkgZnVuY3Rpb24gdGhhdCB3aWxsIGJlIG5vcm1hbGlzZWQgdG8gc3VtIHRvIDENCiMgICAsIHNvIGRvbid0IHdvcnJ5IGFib3V0IHRoYXQgaGVyZQ0KTUFUX0FfV1QgPSAxDQpNQVRfQl9XVCA9IC43NQ0KTUFUX0NfV1QgPSAwLjE1DQoNCiMgc2V0IG9mIG1hdHVyaXRpZXMgdG8gdXNlIGZvciBsYW1iZGEgdmFsdWVzDQpsYW1iZGFfbWF0dXJpdGllcyA8LSBzZXEoMC41LCA1LCAwLjUpDQoNCiMgayBmb3Igay1mb2xkIGV2YWx1YXRpb24NCk5VTV9LX0ZPTERTID0gMTANCg0KYGBgDQoNCiMjIyBMb2FkIEc3IHlpZWxkIGluZm8NCmBgYHtyfQ0KZzdfcmF0ZXNfaW5mbyA8LSBmcmVhZCgiLi4vLi4vZGF0YS9yYXRlc19pbmZvX25vdy5jc3YiKVsNCiAgY291bnRyeSAlaW4lIGMoIlVTIiwgIkNBIiwgIkRFIiwgIkZSIiwgIklUIiwgIlVLIiwgIkpQIikgJiBjbGFzcz09IkdPVlQiDQogICwgLihjb3VudHJ5LCBjbGFzcywgbWF0dXJpdHksIGJiX3RpY2tlcikNCl0NCg0KZzdfcmF0ZXNfZGF0YSA8LSBnN19yYXRlc19pbmZvWw0KICBmcmVhZCgiLi4vLi4vZGF0YS9yYXRlc19kYXRhX25vdy5jc3YiKQ0KICAsIA0KICAsIG9uPS4oYmJfdGlja2VyKSwgbm9tYXRjaD1OVUxMIA0KXVsNCiAgLCAuKGNvdW50cnkNCiAgICAgICwgY2xhc3MNCiAgICAgICwgbWF0dXJpdHkNCiAgICAgICwgZGF0ZT1sdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkNCiAgICAgICwgeWVhcj1sdWJyaWRhdGU6OnllYXIobHVicmlkYXRlOjphc19kYXRlKGRhdGUpKQ0KICAgICAgLCBtb250aD1sdWJyaWRhdGU6Om1vbnRoKGx1YnJpZGF0ZTo6YXNfZGF0ZShkYXRlKSkNCiAgICAgICwgeWllbGQ9UFhfTEFTVA0KICAgICAgKQ0KXVsNCiAgLCAuU0QsIGtleT0uKGNvdW50cnksIGNsYXNzLCBtYXR1cml0eSwgZGF0ZSkNCl0NCg0KDQpnN19jdXJ2ZV9tYXRzIDwtIHNvcnQodW5pcXVlKGc3X3JhdGVzX2RhdGEkbWF0dXJpdHkpKQ0KZzdfY3VydmVfY29sdW1ucyA8LSBwYXN0ZTAoInlpZWxkXyIsIGc3X2N1cnZlX21hdHMpDQoNCmc3X2N1cnZlcyA8LSBkY2FzdCgNCiAgZzdfcmF0ZXNfZGF0YSwgY291bnRyeSArIGNsYXNzICsgZGF0ZSArIHllYXIgKyBtb250aCB+IG1hdHVyaXR5LCB2YWx1ZS52YXI9InlpZWxkIiAgDQopWw0KICAsIC5TRA0KICAsIGtleT0uKGNvdW50cnksIGNsYXNzLCBkYXRlKSAgDQpdDQpzZXRuYW1lcyhnN19jdXJ2ZXMsIGMoa2V5KGc3X2N1cnZlcyksIGc3X2N1cnZlX21hdHMpLCBjKGtleShnN19jdXJ2ZXMpLCBnN19jdXJ2ZV9jb2x1bW5zKSkNCg0KZzdfY3VydmVzW10NCmBgYA0KDQojIyMgTG9hZCBHNyBNb25leSBtYXJrZXQgZGF0YQ0KYGBge3J9DQpnN19tbV9pbmZvIDwtIGRhdGEudGFibGUoDQogIGNvdW50cnk9YygiVVMiLCAiQ0EiLCAiREUiLCAiRlIiLCAiSVQiLCAiVUsiLCAiSlAiLCAiREUiLCAiRlIiLCAiSVQiKQ0KICAsIGN1cm5jeT1jKCJVU0QiLCAiQ0FEIiwgIkVVUiIsICJFVVIiLCAiRVVSIiwgIkdCUCIsICJKUFkiLCAiREVNIiwgIkZSRiIsICJJVEwiKQ0KICAsIGlkPWMoIlVTRC5GSVguMU0iLCAiQ0FELkZJWC4xTSIsICJFVVIuRklYLjFNIiwgIkVVUi5GSVguMU0iLCAiRVVSLkZJWC4xTSINCiAgICAgICAgICwgIkdCUC5GSVguMU0iLCAiSlBZLkZJWC4xTSIsICJERU0uRklYLjFNIiwgIkZSRi5GSVguMU0iLCAiSVRMLkZJWC4xTSIpDQopDQojIGc3X21tX2luZm8gPC0gZnJlYWQoIi4uL2RhdGEvbW1pbmZvLmNzdiIpWw0KIyAgIENVUk5DWSAlaW4lIGMoIlVTRCIsICAiRVVSIiwgIkdCUCIsICJKUFkiLCAiQ0FEIikNCiMgICAsIC4oY3VybmN5PUNVUk5DWSwgaWQ9RklYXzFNKQ0KIyBdDQpnN19tbV9kYXRhIDwtIGc3X21tX2luZm9bDQogIGZyZWFkKCIuLi8uLi9kYXRhL21tZGF0YS5jc3YiKQ0KICAsIA0KICAsIG9uPS4oaWQpLCBub21hdGNoPU5VTEwNCl1bDQogICwgLihjb3VudHJ5DQogICAgICAsIGN1cm5jeQ0KICAgICAgLCBkYXRlID0gbHVicmlkYXRlOjphc19kYXRlKGRhdGUpDQogICAgICAsIHllYXI9bHVicmlkYXRlOjp5ZWFyKGx1YnJpZGF0ZTo6YXNfZGF0ZShkYXRlKSkNCiAgICAgICwgbW9udGg9bHVicmlkYXRlOjptb250aChsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkpDQogICAgICAsIG1hdHVyaXR5ID0gMC44MzMzDQogICAgICAsIHlpZWxkID0gUFhfTEFTVA0KICAgICAgKQ0KXVsNCiAgLCAuU0QgLCBrZXk9Lihjb3VudHJ5LCBjdXJuY3ksIGRhdGUpDQpdDQoNCiMgc3RpdGNoIHRvZ2V0aGVyIHByZS0gYW5kIHBvc3QtRVVSIGNvbnZlcmdlbmNlIGRhdGEgZm9yIERFLCBGUiwgSVQNCmV1cl9zdGFydF9kYXRlIDwtIG1pbihnN19tbV9kYXRhW2NvdW50cnk9PSJERSIgJiBjdXJuY3k9PSJFVVIiXSRkYXRlKQ0KIyBkZWxldGUgbG9jYWwgb2JzZXJ2YXRpb25zIHdoZXJlIEVVUiBkYXRhIGlzIGF2YWlsYWJsZQ0KZzdfbW1fZGF0YSA8LSBnN19tbV9kYXRhWyEoY3VybmN5ICVpbiUgYygiREVNIiwgIkZSRiIsICJJVEwiKSAmIGRhdGUgPj0gZXVyX3N0YXJ0X2RhdGUpXQ0KIyByZWxhYmVsIHByZS1FVVIgbG9jYWwgb2JzZXJ2YXRpb25zIGFzIEVVUg0KIyBtYXkgbmVlZCB0byByZXRoaW5rIHRoaXMgaWYgd2Ugd2FudCB0byBpbXBsZW1lbnQgcHJlLSBhbmQgcG9zdC1FVVIgY3VycmVuY3kgZGF0YQ0KIyAtIG1heWJlIGxhYmVsIGV2ZXJ5dGhpbmcgYXMgREVNIEZSRiBJVEw/DQpnN19tbV9kYXRhW2N1cm5jeSAlaW4lIGMoIkRFTSIsICJGUkYiLCAiSVRMIiksIGN1cm5jeSA6PSAiRVVSIl0gDQoNCmc3X21tX2RhdGFbXQ0KYGBgDQoNCiMjIyBDcmVhdGUgbW9udGhseSBkYXRhDQpgYGB7cn0NCmc3X2N1cnZlc19tIDwtIGc3X2N1cnZlc1sNCiAgZGF0ZSAlaW4lIGNhaW06Om1vbnRoX2VuZF9kYXRlcyhkYXRlKQ0KXQ0KDQpnN19tbV9kYXRhX20gPC0gZzdfbW1fZGF0YVsNCiAgZGF0ZSAlaW4lIGNhaW06Om1vbnRoX2VuZF9kYXRlcyhkYXRlKQ0KXQ0KDQpgYGANCg0KIyMjIEFkZCBtb25leSBtYXJrZXQgZGF0YSB0byBzaG9ydCBlbmQgb2YgZ292ZXJubWVudCBjdXJ2ZXMNClRoaXMgaXMsIGFkbWl0dGVkbHksIGEgYmlnIGtsdWRnZSwgYnV0IHNob3VsZCBoZWxwIHdoZW4gdGhlcmUgYXJlIG5vIGdvdmVybm1lbnQgeWllbGRzIGJlbG93DQoyLXllYXIgbWF0dXJpdHksIGxpa2UgZWFybHkgQ2FuYWRhLCBldGMuIEFsc28sIHRoaXMgbWF5IGJlIGEgZ29vZCB3YXkgdG8gYXZlcmFnZSAzLW1vbnRoIGJpbGxzIGFuZCANCjEtbW9udGggZGVwb3NpdHMgYXMgYSBwcm94eSBmb3IgY2FzaCB3aGVuIGRvaW5nIE5lbHNvbi1TaWVnZWwgYW5hbHlzZXMuDQoNCkFzc3VtcHRpb246IFdlJ2xsIGRvIExJQk9SIC0gMS84IGFzIGEgY3J1ZGUgcHJveHkgZm9yIExJQklEDQpgYGB7cn0NCmc3X2N1cnZlc19tW2c3X21tX2RhdGFfbSwgeWllbGRfMC4wODMzIDo9IGkueWllbGQgLSAwLjEyNSwgb249Lihjb3VudHJ5LCB5ZWFyLCBtb250aCldW10NCmBgYA0KDQojIyMgRW5zdXJlIHRoZXJlIGFyZSBhdCBsZWFzdCAzIHlpZWxkIHBvaW50cw0KYGBge3J9DQojIGVuc3VyZSB0aGVyZSBhcmUgYXQgbGVhc3QgMyB5aWVsZCBwb2ludHMNCmc3X2N1cnZlc19tIDwtIGc3X2N1cnZlc19tWw0KICBnN19jdXJ2ZXNfbVsgLC4odmFsaWQ9KHN1bSghaXMubmEoLlNEKSkgPj0gMykpLCBieT0uKGNvdW50cnksIGNsYXNzLCBkYXRlKV0kdmFsaWQNCl1bDQogICMgZmluZCBmaXJzdCBkYXRlIHRoYXQgd29ya3MgZm9yIGFsbCBjb3VudHJpZXMNCiAgZGF0ZSA+PSBtYXgoZzdfY3VydmVzX21bLC4obWluX2RhdGU9bWluKGRhdGUpKSwgYnk9Lihjb3VudHJ5LCBjbGFzcyldJG1pbl9kYXRlKQ0KXQ0KYGBgDQoNCiMjIyBDYWxjdWxhdGUgTmVsc29uIFNpZWdlbCBjb2VmZmljaWVudHMNCmBgYHtyfQ0KY3VydmVfZGF0YSA8LSBnN19jdXJ2ZXNfbQ0KY3VydmVfbWF0cyA8LSBnN19jdXJ2ZV9tYXRzDQpjdXJ2ZV9jb2x1bW5zIDwtIGc3X2N1cnZlX2NvbHVtbnMNCg0KY3VydmVfY29lZnMgPC0gbGlzdCgpDQpjdXJ2ZV9pZHMgPC0gdW5pcXVlKGN1cnZlX2RhdGFbLC4oY291bnRyeSwgY2xhc3MpXSkNCmZvciAoYyBpbiAxOm5yb3coY3VydmVfaWRzKSkgew0KICB0X2N1cnZlX2RhdGEgPC0gY3VydmVfZGF0YVtjb3VudHJ5ID09IGN1cnZlX2lkc1tjLCBjb3VudHJ5XSAmIGNsYXNzID09IGN1cnZlX2lkc1tjLCBjbGFzc11dDQogIHRfY3VydmVfZGF0ZXMgPC0gc29ydCh1bmlxdWUodF9jdXJ2ZV9kYXRhJGRhdGUpKQ0KICB0X2N1cnZlX21hdHMgPC0gY3VydmVfbWF0cyAjc29ydCh1bmlxdWUoeWllbGRfaW5mb1tjdXJ2ZV9pZD09Y10kbWF0dXJpdHkpKQ0KICB0X2N1cnZlX21hdF9uYW1lcyA8LSBjdXJ2ZV9jb2x1bW5zICNhcy5jaGFyYWN0ZXIodF9jdXJ2ZV9tYXRzKQ0KICANCiAgdF9jdXJ2ZV9tYXRfd3RzIDwtIHJlcChNQVRfQ19XVCwgbGVuZ3RoKHRfY3VydmVfbWF0cykpDQogIG5hbWVzKHRfY3VydmVfbWF0X3d0cykgPC0gdF9jdXJ2ZV9tYXRfbmFtZXMNCiAgdF9jdXJ2ZV9tYXRfd3RzW25hbWVzKHRfY3VydmVfbWF0X3d0cykgJWluJSBtYXR1cml0aWVzX2FdIDwtIE1BVF9BX1dUDQogIHRfY3VydmVfbWF0X3d0c1tuYW1lcyh0X2N1cnZlX21hdF93dHMpICVpbiUgbWF0dXJpdGllc19iXSA8LSBNQVRfQl9XVA0KICB0X2N1cnZlX21hdF93dHMgPC0gdF9jdXJ2ZV9tYXRfd3RzIC8gc3VtKHRfY3VydmVfbWF0X3d0cykNCiAgDQogIA0KICB0X3JlcyA8LSBsaXN0KCkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGxhbWJkYV9tYXR1cml0aWVzKSkgew0KICAgIG1hdCA8LSBsYW1iZGFfbWF0dXJpdGllc1tpXQ0KICAgIGxhbWJkYSA8LSBjYWltOjpuc19tYXQybGFtYmRhKG1hdCkNCiAgICBwcmludChwYXN0ZSgiY2FsY3VsYXRpbmcgZm9yIGN1cnZlOiIsIGMsIGN1cnZlX2lkc1tjLCBjb3VudHJ5XSwgY3VydmVfaWRzW2MsIGNsYXNzXSwgIm1hdHVyaXR5OiIsIG1hdCwgImxhbWJkYToiLCBsYW1iZGEpKQ0KICAgIHRfcmVzW1tpXV0gPC0gbGlzdCgNCiAgICAgIG1hdD1tYXQNCiAgICAgICwgbGFtYmRhPWxhbWJkYQ0KICAgICAgLCBjb2Vmcz1kYXRhLnRhYmxlKA0KICAgICAgICBjb3VudHJ5PWN1cnZlX2lkc1tjLCBjb3VudHJ5XQ0KICAgICAgICAsIGNsYXNzPWN1cnZlX2lkc1tjLCBjbGFzc10NCiAgICAgICAgLCBkYXRlPXRfY3VydmVfZGF0YVssZGF0ZV0NCiAgICAgICAgLCB0KGFwcGx5KHRfY3VydmVfZGF0YSwgMSwgZnVuY3Rpb24oeCkgDQogICAgICAgICAgY2FpbTo6bnNfeWllbGRzMmNvZWZzKHRfY3VydmVfbWF0cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIGFzLm51bWVyaWMoeFt0X2N1cnZlX21hdF9uYW1lc10pDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgbGFtYmRhPWxhbWJkYQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHd0cz10X2N1cnZlX21hdF93dHMpKSkNCiAgICAgICAgLCBsYW1iZGFfbWF0PW1hdA0KICAgICAgICApDQogICAgICApDQogIH0NCiAgDQogICMgY2FsY3VsYXRlIHN1bW1hcmllcw0KICAjIDUgeWVhciBoYWxmbGlmZSBhc3N1bWluZyAyNjAgYnVzaW5lc3MgZGF5cyBpbiBhIHllYXINCiAgZGVjYXkgPC0gaGFsZmxpZmUyZGVjYXkoNSoyNjApDQogIGxhbWJkYV9zdW1tYXJ5IDwtIGRhdGEudGFibGUodChzYXBwbHkodF9yZXMsIGZ1bmN0aW9uKHgpIA0KICAgIGMobWF0PXgkbWF0LCBsYW1iZGE9eCRsYW1iZGEsIHdzcz1tZWFuKHgkY29lZnMkd3NzKSwgd3NzX2V4cD1tZWFuX2V4cCh4JGNvZWZzJHdzcywgZGVjYXkpKSkpKQ0KDQogICMgY2hvb3NlIGJlc3QgZGF0YQ0KICBiZXN0X2hpc3RfZml0IDwtIHdoaWNoKGxhbWJkYV9zdW1tYXJ5JHdzcyA9PSBtaW4obGFtYmRhX3N1bW1hcnkkd3NzKSkNCiAgYmVzdF9leHBfZml0IDwtIHdoaWNoKGxhbWJkYV9zdW1tYXJ5JHdzc19leHAgPT0gbWluKGxhbWJkYV9zdW1tYXJ5JHdzc19leHApKQ0KICAjIHRha2UgYXZlcmFnZSBvZiBiZXN0IGZpdHMsIGJ1dCB0aWx0IHRvd2FyZHMgYmVzdF9leHBfZml0DQogIGJlc3RfaXggPC0gZmxvb3IobWVhbihjKGJlc3RfaGlzdF9maXQsIGJlc3RfZXhwX2ZpdCkpK2lmZWxzZShiZXN0X2V4cF9maXQgPiBiZXN0X2hpc3RfZml0LCAwLjUsIDApKQ0KICBiZXN0X2hpc3RfY29lZnMgPC0gdF9yZXNbW2Jlc3RfaXhdXSRjb2Vmcw0KICAjIGJlc3RfaGlzdF9jb2VmcyRjdXJ2ZV9pZCA8LSBjDQoNCiAgI2FkZCBkYXRhIHRvIGN1cnZlX2NvZWZzDQogIGN1cnZlX2NvZWZzW1tsZW5ndGgoY3VydmVfY29lZnMpKzFdXSA8LSBiZXN0X2hpc3RfY29lZnMNCn0NCg0KbnNfY29lZnMgPC0gcmJpbmRsaXN0KGN1cnZlX2NvZWZzKVsNCiAgLCAuKG5zX2JldGEwID0gYmV0YTANCiAgICAgICwgbnNfYmV0YTEgPSBiZXRhMQ0KICAgICAgLCBuc19iZXRhMiA9IGJldGEyDQogICAgICAsIG5zX2xhbWJkYSA9IGxhbWJkYQ0KICAgICAgLCBuc19sYW1iZGFfbWF0ID0gbGFtYmRhX21hdA0KICAgICAgLCBuc193c3MgPSB3c3MNCiAgICAgICkNCiAgLCBrZXk9Lihjb3VudHJ5LCBjbGFzcywgZGF0ZSkNCl0NCg0KZzdfY3VydmVzX20gPC0gZzdfY3VydmVzX21bbnNfY29lZnNdDQoNCnJtKGxpc3Q9c2V0ZGlmZihscygpLCBjKCJOVU1fS19GT0xEUyIsICJnN19jdXJ2ZXMiLCAiZzdfY3VydmVzX20iLCAiZzdfcmF0ZXNfZGF0YSIsICJnN19yYXRlc19pbmZvIiwgImc3X2N1cnZlX2NvbHVtbnMiLCAiZzdfY3VydmVfbWF0cyIpKSkNCmBgYA0KDQojIyMgWWllbGQgY2FsY3VsYXRpb25zDQpgYGB7cn0NCmxvbmdfZGF0YSA8LSBtZWx0KA0KICBnN19jdXJ2ZXNfbSAgDQogICwgYygiY291bnRyeSIsICJjbGFzcyIsICJkYXRlIiwgIm5zX2JldGEwIiwgIm5zX2JldGExIiwgIm5zX2JldGEyIiwgIm5zX2xhbWJkYSIpDQogICwgIHBhdHRlcm5zKCJ5aWVsZF8iKQ0KICAsIHZhbHVlLm5hbWU9InlpZWxkX25vdyINCilbDQogICwgbWF0dXJpdHkgOj0gYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfcmVtb3ZlKHZhcmlhYmxlLCAieWllbGRfIikpDQpdWw0KICAsIC4oY291bnRyeSwgY2xhc3MsIGRhdGUsIG5zX2JldGEwLCBuc19iZXRhMSwgbnNfYmV0YTIsIG5zX2xhbWJkYSwgbWF0dXJpdHksIHlpZWxkX25vdykNCl0NCg0KIyBmaWxsIGluIG1pc3NpbmcgeWllbGRzIHdpdGggaW50ZXJwb2xhdGVkIGRhdGENCiMgdGFibGUgd2l0aCAhaXMubmEoeWllbGQpDQp5aWVsZHNfdmFsaWQgPC0gbG9uZ19kYXRhWyFpcy5uYSh5aWVsZF9ub3cpXVssIGMoIm1hdCIsICJ5bGQiKSA6PSAuKG1hdHVyaXR5LCB5aWVsZF9ub3cpXQ0KIyB0YWJsZSBtYXBwaW5nIHRvIDw9DQp5aWVsZHNfbG8gPC0geWllbGRzX3ZhbGlkW2xvbmdfZGF0YSwgLihjb3VudHJ5LCBkYXRlLCBtYXR1cml0eSwgbV9sbzA9bWF0LCB5X2xvMD15bGQpLCBvbj0uKGNvdW50cnksIGRhdGUsIG1hdHVyaXR5KSwgcm9sbD1UXQ0KIyB0YWJsZSBtYXBwaW5nIHRvID49DQp5aWVsZHNfaGkgPC0geWllbGRzX3ZhbGlkW2xvbmdfZGF0YSwgLihjb3VudHJ5LCBkYXRlLCBtYXR1cml0eSwgbV9oaTA9bWF0LCB5X2hpMD15bGQpLCBvbj0uKGNvdW50cnksIGRhdGUsIG1hdHVyaXR5KSwgcm9sbD0tSW5mXQ0KDQp5aWVsZHNfbGluIDwtIHlpZWxkc19oaVsNCiAgeWllbGRzX2xvDQogICwgLigNCiAgICBjb3VudHJ5DQogICAgLCBkYXRlDQogICAgLCBtYXR1cml0eQ0KICAgICwgeWllbGRfbGluID0gaWZlbHNlKG1faGkwID09IG1fbG8wDQogICAgICAgICAgICAgICAgICAgICAgICAgLCB5X2hpMA0KICAgICAgICAgICAgICAgICAgICAgICAgICwgeV9sbzAgKyAobWF0dXJpdHkgLSBtX2xvMCkgKiAoeV9oaTAgLSB5X2xvMCkgLyAobV9oaTAgLSBtX2xvMCkNCiAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgLCB5X2hpMA0KICAgICwgeV9sbzANCiAgICAsIG1faGkwDQogICAgLCBtX2xvMA0KICApDQogICwgb249Lihjb3VudHJ5LCBkYXRlLCBtYXR1cml0eSkNCl0NCg0KbG9uZ19kYXRhIDwtIGxvbmdfZGF0YVt5aWVsZHNfbGluLCBvbj0uKGNvdW50cnksIGRhdGUsIG1hdHVyaXR5KV0NCg0KbG9uZ19kYXRhIDwtIGxvbmdfZGF0YVsNCiAgLCBjKCJudW1fY291cHMiLCAibW9kX2R1ciIpIDo9IC4oDQogICAgaWZlbHNlKG1hdHVyaXR5ID4gMC4yNSwgaWZlbHNlKGNvdW50cnk9PSJVUyIsIDIsIDEpLCAwKQ0KICAgICwgbWF0dXJpdHkNCiAgKSAgDQpdWw0KICBtYXR1cml0eSA+IDAuMjUNCiAgLCBtb2RfZHVyIDo9IHJvdW5kKGNhaW06Om1vZGlmaWVkX2R1cmF0aW9uKHlpZWxkX2xpbiAvIDEwMCwgeWllbGRfbGluIC8gMTAwLCBtYXR1cml0eSwgbnVtX2NvdXBzKSwgNikNCl1bDQogICwgLigNCiAgICBuc19iZXRhMA0KICAgICwgbnNfYmV0YTENCiAgICAsIG5zX2JldGEyDQogICAgLCBuc19sYW1iZGENCiAgICAsIG51bV9jb3Vwcw0KICAgICwgbW9kX2R1ciAjPSByb3VuZChjYWltOjptb2RpZmllZF9kdXJhdGlvbih5aWVsZF9ub3cgLyAxMDAsIHlpZWxkX25vdyAvIDEwMCwgbWF0dXJpdHksIDEpLCA2KQ0KICAgICwgeWllbGRfbm93DQogICAgLCB5aWVsZF9saW4NCiAgICAsIHlfaGkwDQogICAgLCB5X2xvMA0KICAgICwgbV9oaTANCiAgICAsIG1fbG8wDQogICAgKQ0KICAsIGtleT0uKGNvdW50cnksIGNsYXNzLCBtYXR1cml0eSwgZGF0ZSkNCl1bDQogICwgeWllbGRfcHJldiA6PSBzaGlmdCh5aWVsZF9saW4pLCBieSA9IC4oY291bnRyeSwgY2xhc3MsIG1hdHVyaXR5KQ0KXVsNCiAgLCBjKCJ5X2hpMSIsICJ5X2xvMSIsICJtX2hpMSIsICJtX2xvMSIpIDo9IC4oDQogICAgeWllbGRfbGluDQogICAgLCBzaGlmdCh5aWVsZF9saW4pDQogICAgLCBtYXR1cml0eQ0KICAgICwgc2hpZnQobWF0dXJpdHkpDQogICkNCiAgLCBieSA9IC4oY291bnRyeSwgY2xhc3MsIGRhdGUpDQpdWw0KICAsIHlpZWxkX3NlbGxfbGluIDo9ICgNCiAgICB5X2xvMSArICgobV9oaTEgLSAxLzEyKSAtIG1fbG8xKSAqICh5X2hpMSAtIHlfbG8xKSAvIChtX2hpMSAtIG1fbG8xKQ0KICApDQpdWw0KICAsIHlpZWxkX2J1eV9ucyA6PSByb3VuZChjYWltOjpuc19jb2VmczJ5aWVsZHMoDQogICAgbWF0cyA9IG1hdHVyaXR5DQogICAgLCBiZXRhMCA9IHNoaWZ0KG5zX2JldGEwKQ0KICAgICwgYmV0YTEgPSBzaGlmdChuc19iZXRhMSkNCiAgICAsIGJldGEyID0gc2hpZnQobnNfYmV0YTIpDQogICAgLCBsYW1iZGEgPSBzaGlmdChuc19sYW1iZGEpDQogICkkeSwgNCkNCiAgLCBieSA9IC4oY291bnRyeSwgY2xhc3MsIG1hdHVyaXR5KQ0KXVsNCiAgLCB5aWVsZF9zZWxsX25zIDo9IHJvdW5kKGNhaW06Om5zX2NvZWZzMnlpZWxkcygNCiAgICBtYXRzID0gbWF0dXJpdHkgLSAxLzINCiAgICAsIGJldGEwID0gbnNfYmV0YTANCiAgICAsIGJldGExID0gbnNfYmV0YTENCiAgICAsIGJldGEyID0gbnNfYmV0YTINCiAgICAsIGxhbWJkYSA9IG5zX2xhbWJkYQ0KICApJHksIDQpDQpdWw0KICAsIGNvdXBfaW5jX2xpbiA6PSByb3VuZCh5aWVsZF9wcmV2IC8gMTIwMCwgNikNCl1bDQogICwgc2hpZnRfaW5jIDo9IGlmZWxzZShtYXR1cml0eSA+IDAuMjUsIHJvdW5kKCh5aWVsZF9saW4gLSB5aWVsZF9wcmV2KSAvIDEwMCAqIC1tb2RfZHVyLCA2KSwgMCkNCl1bDQogICwgdG90X3JldF9zaGlmdCA6PSBjb3VwX2luY19saW4gKyBzaGlmdF9pbmMNCl1bDQogICwgZHVyX2luY19saW4gOj0gaWZlbHNlKA0KICAgIG1hdHVyaXR5ID4gMC4yNQ0KICAgICwgcm91bmQoY2FpbTo6Ym9uZF9wcmljZSh5aWVsZF9zZWxsX2xpbiAvIDEwMCwgeWllbGRfcHJldiAvIDEwMCwgbWF0dXJpdHkgLSAxLzEyLCAxLCAxKSAtIDEsIDYpDQogICAgLCAwDQogICAgKQ0KXVsNCiAgLCB0b3RfcmV0X2xpbiA6PSBjb3VwX2luY19saW4gKyBkdXJfaW5jX2xpbg0KXVsNCiAgLCBjb3VwX2luY19ucyA6PSByb3VuZCh5aWVsZF9idXlfbnMgLyAxMjAwLCA2KSAgDQpdWw0KICwgcHJpY2VfaW5jX25zIDo9IGlmZWxzZSgNCiAgIG1hdHVyaXR5ID4gMC4yNQ0KICAgLCByb3VuZChjYWltOjpib25kX3ByaWNlKHlpZWxkX3NlbGxfbnMgLyAxMDAsIHlpZWxkX2J1eV9ucyAvIDEwMCwgbWF0dXJpdHkgLSAxLzEyLCAxLCAxKSAtIDEsIDYpDQogICAsIDANCiAgICkNCl1bDQogICwgdG90X3JldF9ucyA6PSBjb3VwX2luY19ucyArIHByaWNlX2luY19ucw0KXQ0KDQpsb25nX2RhdGFbY291bnRyeT09IlVTIl0NCg0Kd2lkZV9kYXRhIDwtIGRjYXN0KA0KICBsb25nX2RhdGENCiAgLGNvdW50cnkgKyBjbGFzcyArIGRhdGUgKyBuc19iZXRhMCArIG5zX2JldGExICsgbnNfYmV0YTIgKyBuc19sYW1iZGEgfiBtYXR1cml0eQ0KICAsIHZhbHVlLnZhciA9IG5hbWVzKGxvbmdfZGF0YSlbIShuYW1lcyhsb25nX2RhdGEpICVpbiUgYygiY291bnRyeSIsICJjbGFzcyIsICJkYXRlIiwgIm5zX2JldGEwIiwgIm5zX2JldGExIiwgIm5zX2JldGEyIiwgIm5zX2xhbWJkYSIsICJtYXR1cml0eSIpKV0NCg0KICAjICwgdmFsdWUudmFyID0gYygibW9kX2R1ciIsICJ5aWVsZF9ub3ciLCAieWllbGRfcHJldiIsICJ5aWVsZF9idXlfbnMiLCAieWllbGRfc2VsbF9ucyINCiAgIyAgICAgICAgICAgICAgICAgLCAiY291cF9pbmNfbGluIiwgImR1cl9pbmNfbGluIiwgImNvdXBfaW5jX25zIiwgInByaWNlX2luY19ucyIsICJ0b3RfcmV0X25zIg0KICAjICAgICAgICAgICAgICAgICApDQogICkNCg0Kd2lkZV9kYXRhW10NCg0KZzdfcmV0dXJuX2RhdGEgPC0gZGNhc3QoDQogIGxvbmdfZGF0YVttYXR1cml0eSAlaW4lIGMoMC4wODMzLCAxLCAyLCAzLCA1LCA3LCAxMCldDQogICwgY291bnRyeSArIGNsYXNzICsgZGF0ZSB+IG1hdHVyaXR5DQogICwgdmFsdWUudmFyID0gYygiY291cF9pbmNfbGluIiwgInRvdF9yZXRfc2hpZnQiLCAidG90X3JldF9saW4iLCAidG90X3JldF9ucyIpDQopDQoNCmc3X3JldHVybl9kYXRhW10NCmBgYA0KDQojIyMgR2V0IGFzc2V0IGRhdGENCmBgYHtyfQ0KZzdfYXNzZXRzIDwtIGRhdGEudGFibGUoDQogIGlkPWMoIlVTLkdPVlQuMTMiLCAiVVMuR09WVC4xNSIsICJVUy5HT1ZULjExMCINCiAgICAgICAsICJDQS5HT1ZULjEzIiwgIkNBLkdPVlQuMTUiLCAiQ0EuR09WVC4xMTAiDQogICAgICAgLCAiREUuR09WVC4xMyIsICJERS5HT1ZULjE1IiwgIkRFLkdPVlQuMTEwIg0KICAgICAgICwgIkZSLkdPVlQuMTMiLCAiRlIuR09WVC4xNSIsICJGUi5HT1ZULjExMCINCiAgICAgICAsICJJVC5HT1ZULjEzIiwgIklULkdPVlQuMTUiLCAiSVQuR09WVC4xMTAiDQogICAgICAgLCAiVUsuR09WVC4xMyIsICJVSy5HT1ZULjE1IiwgIlVLLkdPVlQuMTEwIg0KICAgICAgICwgIkpQLkdPVlQuMTMiLCAiSlAuR09WVC4xNSIsICJKUC5HT1ZULjExMCINCiAgICAgICApDQogICwgY291bnRyeT1jKHJlcCgiVVMiLCAzKSwgcmVwKCJDQSIsIDMpLCByZXAoIkRFIiwgMyksIHJlcCgiRlIiLCAzKSwgcmVwKCJJVCIsIDMpDQogICAgICAgICAgICAgICwgcmVwKCJVSyIsIDMpLCByZXAoIkpQIiwgMykpDQogICwgbWF0dXJpdHk9cmVwKGMoMTMsIDE1LCAxMTApLCA3KQ0KICAsIGtleT1jKCJjb3VudHJ5IiwgIm1hdHVyaXR5IikNCikNCmc3X2Fzc2V0X2luZm8gPC0gZnJlYWQoIi4uLy4uL2RhdGEvYXNzZXRpbmZvLmNzdiIpW3N1Z2duYW1lICVpbiUgZzdfYXNzZXRzJGlkXQ0KDQpnN19hc3NldF9pbmZvW10NCg0KZzdfYXNzZXRfZGF0YSA8LSBnN19hc3NldHNbDQogIGZyZWFkKCIuLi8uLi9kYXRhL2Fzc2V0ZGF0YS5jc3YiKQ0KICAsIC4oDQogICAgY291bnRyeQ0KICAgICwgbWF0dXJpdHkNCiAgICAsIGlkDQogICAgLCBkYXRlID0gbHVicmlkYXRlOjphc19kYXRlKGRhdGUpDQogICAgLCB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKGRhdGUpDQogICAgLCBtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoZGF0ZSkNCiAgICAsIGluZGV4X25hdj1QWF9MQVNUDQogICAgKQ0KICAsIG9uPS4oaWQpLCBub21hdGNoPU5VTEwNCl1bDQogIGRhdGUgJWluJSBjYWltOjptb250aF9lbmRfZGF0ZXMoZGF0ZSkNCiAgLCAuU0QNCiAgLCBrZXk9Lihjb3VudHJ5LCBtYXR1cml0eSwgZGF0ZSkNCl1bDQogICwgaW5kZXhfcmV0IDo9IHJvdW5kKGluZGV4X25hdiAvIHNoaWZ0KGluZGV4X25hdikgLSAxLCA2KQ0KICAsIGJ5PS4oY291bnRyeSwgbWF0dXJpdHkpDQpdDQoNCmc3X2Fzc2V0X2RhdGFbXQ0KYGBgDQoNCg0KIyMjIE1vZGVsIFNldHVwDQpgYGB7cn0NCmc3X21vZGVscyA8LSBsaXN0KA0KICBsaXN0KG5hbWU9InhfZzEzX2NvdXBfYnVsbGV0IiwgdHlwZT0iY291cF9idWxsZXQiLCBtYXR1cml0eT0xMw0KICAgICAgICwgZmVhdHVyZXM9YygiY291cF9pbmNfbGluXzIiKSwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzEzX3NoaWZ0X2J1bGxldCIsIHR5cGU9InNoaWZ0X2J1bGxldCIsIG1hdHVyaXR5PTEzDQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfc2hpZnRfMiIpLCB1bml0eV9jb2Vmcz1UKQ0KICAsIGxpc3QobmFtZT0ieF9nMTNfbGluX2J1bGxldCIsIHR5cGU9Imxpbl9idWxsZXQiLCBtYXR1cml0eT0xMw0KICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X2xpbl8yIiksIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxM19uc19idWxsZXQiLCB0eXBlPSJuc19idWxsZXQiLCBtYXR1cml0eT0xMw0KICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X25zXzIiKSwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzEzX2NvdXBfbiIsIHR5cGU9ImNvdXBfbiIsIG1hdHVyaXR5PTEzDQogICAgICAgICAsIGZlYXR1cmVzPWMoImNvdXBfaW5jX2xpbl8xIiwgImNvdXBfaW5jX2xpbl8yIiwgImNvdXBfaW5jX2xpbl8zIiksIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxM19zaGlmdF9uIiwgdHlwZT0ic2hpZnRfbiIsIG1hdHVyaXR5PTEzDQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfc2hpZnRfMSIsICJ0b3RfcmV0X3NoaWZ0XzIiLCAidG90X3JldF9zaGlmdF8zIiksIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxM19saW5fbiIsIHR5cGU9Imxpbl9uIiwgbWF0dXJpdHk9MTMNCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9saW5fMSIsICJ0b3RfcmV0X2xpbl8yIiwgInRvdF9yZXRfbGluXzMiKSwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzEzX25zX24iLCB0eXBlPSJuc19uIiwgbWF0dXJpdHk9MTMNCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9uc18xIiwgInRvdF9yZXRfbnNfMiIsICJ0b3RfcmV0X25zXzMiKSwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzEzX2NvdXBfbGFkZGVyIiwgdHlwZT0iY291cF9sYWRkZXIiLCBtYXR1cml0eT0xMw0KICAgICAgICAgLCBmZWF0dXJlcz1jKCJjb3VwX2luY19saW5fMSIsICJjb3VwX2luY19saW5fMiIsICJjb3VwX2luY19saW5fMyIpLCB1bml0eV9jb2Vmcz1GKQ0KICAsIGxpc3QobmFtZT0ieF9nMTNfc2hpZnRfbGFkZGVyIiwgdHlwZT0ic2hpZnRfbGFkZGVyIiwgbWF0dXJpdHk9MTMNCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8xIiwgInRvdF9yZXRfc2hpZnRfMiIsICJ0b3RfcmV0X3NoaWZ0XzMiKSwgdW5pdHlfY29lZnM9RikNCiAgLCBsaXN0KG5hbWU9InhfZzEzX2xpbl9sYWRkZXIiLCB0eXBlPSJsaW5fbGFkZGVyIiwgbWF0dXJpdHk9MTMNCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9saW5fMSIsICJ0b3RfcmV0X2xpbl8yIiwgInRvdF9yZXRfbGluXzMiKSwgdW5pdHlfY29lZnM9RikNCiAgLCBsaXN0KG5hbWU9InhfZzEzX25zX2xhZGRlciIsIHR5cGU9Im5zX2xhZGRlciIsIG1hdHVyaXR5PTEzDQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbnNfMSIsICJ0b3RfcmV0X25zXzIiLCAidG90X3JldF9uc18zIiksIHVuaXR5X2NvZWZzPUYpDQoNCiAgLCBsaXN0KG5hbWU9InhfZzE1X2NvdXBfYnVsbGV0IiwgdHlwZT0iY291cF9idWxsZXQiLCBtYXR1cml0eT0xNQ0KICAgICAgICAgLCBmZWF0dXJlcz1jKCJjb3VwX2luY19saW5fMyIpLCB1bml0eV9jb2Vmcz1UKQ0KICAsIGxpc3QobmFtZT0ieF9nMTVfc2hpZnRfYnVsbGV0IiwgdHlwZT0ic2hpZnRfYnVsbGV0IiwgbWF0dXJpdHk9MTUNCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8zIiksIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxNV9saW5fYnVsbGV0IiwgdHlwZT0ibGluX2J1bGxldCIsIG1hdHVyaXR5PTE1DQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbGluXzMiKSwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzE1X25zX2J1bGxldCIsIHR5cGU9Im5zX2J1bGxldCIsIG1hdHVyaXR5PTE1DQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbnNfMyIpLCB1bml0eV9jb2Vmcz1UKQ0KICAsIGxpc3QobmFtZT0ieF9nMTVfY291cF9uIiwgdHlwZT0iY291cF9uIiwgbWF0dXJpdHk9MTUNCiAgICAgICAgICwgZmVhdHVyZXM9YygiY291cF9pbmNfbGluXzEiLCAiY291cF9pbmNfbGluXzIiLCAiY291cF9pbmNfbGluXzMiLCAiY291cF9pbmNfbGluXzUiKSwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzE1X3NoaWZ0X24iLCB0eXBlPSJzaGlmdF9uIiwgbWF0dXJpdHk9MTUNCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8xIiwgInRvdF9yZXRfc2hpZnRfMiIsICJ0b3RfcmV0X3NoaWZ0XzMiLCAidG90X3JldF9zaGlmdF81IiksIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxNV9saW5fbiIsIHR5cGU9Imxpbl9uIiwgbWF0dXJpdHk9MTUNCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9saW5fMSIsICJ0b3RfcmV0X2xpbl8yIiwgInRvdF9yZXRfbGluXzMiLCAidG90X3JldF9saW5fNSIpLCB1bml0eV9jb2Vmcz1UKQ0KICAsIGxpc3QobmFtZT0ieF9nMTVfbnNfbiIsIHR5cGU9Im5zX24iLCBtYXR1cml0eT0xNQ0KICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X25zXzEiLCAidG90X3JldF9uc18yIiwgInRvdF9yZXRfbnNfMyIsICJ0b3RfcmV0X25zXzUiKSwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzE1X2NvdXBfbGFkZGVyIiwgdHlwZT0iY291cF9sYWRkZXIiLCBtYXR1cml0eT0xNQ0KICAgICAgICAgLCBmZWF0dXJlcz1jKCJjb3VwX2luY19saW5fMSIsICJjb3VwX2luY19saW5fMiIsICJjb3VwX2luY19saW5fMyIsICJjb3VwX2luY19saW5fNSIpLCB1bml0eV9jb2Vmcz1GKQ0KICAsIGxpc3QobmFtZT0ieF9nMTVfc2hpZnRfbGFkZGVyIiwgdHlwZT0ic2hpZnRfbGFkZGVyIiwgbWF0dXJpdHk9MTUNCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8xIiwgInRvdF9yZXRfc2hpZnRfMiIsICJ0b3RfcmV0X3NoaWZ0XzMiLCAidG90X3JldF9zaGlmdF81IiksIHVuaXR5X2NvZWZzPUYpDQogICwgbGlzdChuYW1lPSJ4X2cxNV9saW5fbGFkZGVyIiwgdHlwZT0ibGluX2xhZGRlciIsIG1hdHVyaXR5PTE1DQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbGluXzEiLCAidG90X3JldF9saW5fMiIsICJ0b3RfcmV0X2xpbl8zIiwgInRvdF9yZXRfbGluXzUiKSwgdW5pdHlfY29lZnM9RikNCiAgLCBsaXN0KG5hbWU9InhfZzE1X25zX2xhZGRlciIsIHR5cGU9Im5zX2xhZGRlciIsIG1hdHVyaXR5PTE1DQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbnNfMSIsICJ0b3RfcmV0X25zXzIiLCAidG90X3JldF9uc18zIiwgInRvdF9yZXRfbnNfNSIpLCB1bml0eV9jb2Vmcz1GKQ0KICANCiAgLCBsaXN0KG5hbWU9InhfZzExMF9jb3VwX2J1bGxldCIsIHR5cGU9ImNvdXBfYnVsbGV0IiwgbWF0dXJpdHk9MTEwDQogICAgICAgICAsIGZlYXR1cmVzPWMoImNvdXBfaW5jX2xpbl81IiksIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxMTBfc2hpZnRfYnVsbGV0IiwgdHlwZT0ic2hpZnRfYnVsbGV0IiwgbWF0dXJpdHk9MTEwDQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfc2hpZnRfNSIpLCB1bml0eV9jb2Vmcz1UKQ0KICAsIGxpc3QobmFtZT0ieF9nMTEwX2xpbl9idWxsZXQiLCB0eXBlPSJsaW5fYnVsbGV0IiwgbWF0dXJpdHk9MTEwDQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbGluXzUiKSwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzExMF9uc19idWxsZXQiLCB0eXBlPSJuc19idWxsZXQiLCBtYXR1cml0eT0xMTANCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9uc181IiksIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxMTBfY291cF9uIiwgdHlwZT0iY291cF9uIiwgbWF0dXJpdHk9MTEwDQogICAgICAgICAsIGZlYXR1cmVzPWMoImNvdXBfaW5jX2xpbl8xIiwgImNvdXBfaW5jX2xpbl8yIiwgImNvdXBfaW5jX2xpbl8zIiwgImNvdXBfaW5jX2xpbl81IiwgImNvdXBfaW5jX2xpbl83IiwgImNvdXBfaW5jX2xpbl8xMCIpDQogICAgICAgICAsIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxMTBfc2hpZnRfbiIsIHR5cGU9InNoaWZ0X24iLCBtYXR1cml0eT0xMTANCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8xIiwgInRvdF9yZXRfc2hpZnRfMiIsICJ0b3RfcmV0X3NoaWZ0XzMiLCAidG90X3JldF9zaGlmdF81IiwgInRvdF9yZXRfc2hpZnRfNyIsICJ0b3RfcmV0X3NoaWZ0XzEwIikNCiAgICAgICAgICwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzExMF9saW5fbiIsIHR5cGU9Imxpbl9uIiwgbWF0dXJpdHk9MTEwDQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbGluXzEiLCAidG90X3JldF9saW5fMiIsICJ0b3RfcmV0X2xpbl8zIiwgInRvdF9yZXRfbGluXzUiLCAidG90X3JldF9saW5fNyIsICJ0b3RfcmV0X2xpbl8xMCIpDQogICAgICAgICAsIHVuaXR5X2NvZWZzPVQpDQogICwgbGlzdChuYW1lPSJ4X2cxMTBfbnNfbiIsIHR5cGU9Im5zX24iLCBtYXR1cml0eT0xMTANCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9uc18xIiwgInRvdF9yZXRfbnNfMiIsICJ0b3RfcmV0X25zXzMiLCAidG90X3JldF9uc181IiwgInRvdF9yZXRfbnNfNyIsICJ0b3RfcmV0X25zXzEwIikNCiAgICAgICAgICwgdW5pdHlfY29lZnM9VCkNCiAgLCBsaXN0KG5hbWU9InhfZzExMF9jb3VwX2xhZGRlciIsIHR5cGU9ImNvdXBfbGFkZGVyIiwgbWF0dXJpdHk9MTEwDQogICAgICAgICAsIGZlYXR1cmVzPWMoImNvdXBfaW5jX2xpbl8xIiwgImNvdXBfaW5jX2xpbl8yIiwgImNvdXBfaW5jX2xpbl8zIiwgImNvdXBfaW5jX2xpbl81IiwgImNvdXBfaW5jX2xpbl83IiwgImNvdXBfaW5jX2xpbl8xMCIpDQogICAgICAgICAsIHVuaXR5X2NvZWZzPUYpDQogICwgbGlzdChuYW1lPSJ4X2cxMTBfc2hpZnRfbGFkZGVyIiwgdHlwZT0ic2hpZnRfbGFkZGVyIiwgbWF0dXJpdHk9MTEwDQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfc2hpZnRfMSIsICJ0b3RfcmV0X3NoaWZ0XzIiLCAidG90X3JldF9zaGlmdF8zIiwgInRvdF9yZXRfc2hpZnRfNSIsICJ0b3RfcmV0X3NoaWZ0XzciLCAidG90X3JldF9zaGlmdF8xMCIpDQogICAgICAgICAsIHVuaXR5X2NvZWZzPUYpDQogICwgbGlzdChuYW1lPSJ4X2cxMTBfbGluX2xhZGRlciIsIHR5cGU9Imxpbl9sYWRkZXIiLCBtYXR1cml0eT0xMTANCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9saW5fMSIsICJ0b3RfcmV0X2xpbl8yIiwgInRvdF9yZXRfbGluXzMiLCAidG90X3JldF9saW5fNSIsICJ0b3RfcmV0X2xpbl83IiwgInRvdF9yZXRfbGluXzEwIikNCiAgICAgICAgICwgdW5pdHlfY29lZnM9RikNCiAgLCBsaXN0KG5hbWU9InhfZzExMF9uc19sYWRkZXIiLCB0eXBlPSJuc19sYWRkZXIiLCBtYXR1cml0eT0xMTANCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9uc18xIiwgInRvdF9yZXRfbnNfMiIsICJ0b3RfcmV0X25zXzMiLCAidG90X3JldF9uc181IiwgInRvdF9yZXRfbnNfNyIsICJ0b3RfcmV0X25zXzEwIikNCiAgICAgICAgICwgdW5pdHlfY29lZnM9RikNCikNCg0KZzdfZmVhdHVyZXMgPC0gc29ydCh1bmlxdWUocmJpbmRsaXN0KGxhcHBseShnN19tb2RlbHMsIGZ1bmN0aW9uKHgpIGRhdGEudGFibGUoZmVhdHVyZT14JGZlYXR1cmVzKSkpKSRmZWF0dXJlKQ0KDQpgYGANCg0KIyMjIE1vZGVsIGV2YWx1YXRpb24gLSBmdWxsIGhpc3RvcnkNCmBgYHtyfQ0KZXZhbHVhdGUgPC0gZnVuY3Rpb24obW9kZWwsIHBlcl9pbl95ZWFyPTEyLCB3dHM9TlVMTCkgew0KICBpZiAoaXMubnVsbCh3dHMpKQ0KICAgIHd0cyA8LSByZXAoMS9sZW5ndGgobW9kZWwkeSksIGxlbmd0aChtb2RlbCR5KSkNCiAgZWxzZQ0KICAgIHd0cyA8LSB3dHMgLyBzdW0od3RzKQ0KICANCiAgeV9oYXQgPC0gbW9kZWwkWCAlKiUgbW9kZWwkY29lZnMNCiAgcmVzaWR1YWxzIDwtIHlfaGF0IC0gbW9kZWwkeQ0KICByZXR1cm4oDQogICAgYygNCiAgICAgIFIyID0gY2FpbTo6d3RkX2Nvcih5X2hhdCwgbW9kZWwkeSwgd3RzPXd0cykgXiAyDQogICAgICAjICwgTVNFID0gY2FpbTo6d3RkX21lYW4ocmVzaWR1YWxzXjIsIHd0cz13dHMpICMgY2FpbTo6d3RkX21lYW4ocmVzaWR1YWxzXjIsIHd0cz13dHMpDQogICAgICAsIFJNU0UgPSBzcXJ0KGNhaW06Ond0ZF9tZWFuKHJlc2lkdWFsc14yLCB3dHM9d3RzKSkNCiAgICAgICMgLCBhbHBoYSA9IG1lYW4ocmVzaWR1YWxzKSAjIGNhaW06Ond0ZF9tZWFuKHJlc2lkdWFscywgd3RzPXd0cykNCiAgICAgICMgLCB0ZSA9IHNkKHJlc2lkdWFscykgIyBjYWltOjp3dGRfc2QocmVzaWR1YWxzLCB3dHM9d3RzKQ0KICAgICAgLCBhbHBoYV95ID0gKDEgKyBjYWltOjp3dGRfbWVhbihyZXNpZHVhbHMsIHd0cz13dHMpKSBeIHBlcl9pbl95ZWFyIC0gMQ0KICAgICAgLCB0ZV95ID0gY2FpbTo6d3RkX3NkKHJlc2lkdWFscywgd3RzPXd0cykgKiBzcXJ0KHBlcl9pbl95ZWFyKQ0KICAgICkNCiAgKQ0KfQ0KDQojIGZvciBldmVyeSBjb21iaW5hdGlvbiBvZiBjb3VudHJ5IGFuZCBtYXR1cml0eQ0KaGlzdF9ldmFsX2xpc3QgPC0gYXBwbHkoZzdfYXNzZXRzLCAxLCBmdW5jdGlvbihhc3NldCkgew0KICB5X2RhdGEgPC0gZzdfYXNzZXRfZGF0YVtpZD09YXNzZXRbImlkIl1dDQogIHhfZGF0YSA8LSBnN19yZXR1cm5fZGF0YVtjb3VudHJ5PT1hc3NldFsiY291bnRyeSJdXVsNCiAgICAsIGMoInllYXIiLCAibW9udGgiKSA6PSAuKA0KICAgICAgbHVicmlkYXRlOjp5ZWFyKGRhdGUpDQogICAgICAsIGx1YnJpZGF0ZTo6bW9udGgoZGF0ZSkNCiAgICApDQogIF0NCiAgIyBwcmludChwYXN0ZShhc3NldFsiY291bnRyeSJdLCBhc3NldFsibWF0dXJpdHkiXSwgbnJvdyh5X2RhdGEpLCBucm93KHhfZGF0YSkpKQ0KICBtb2RlbF9kYXRhIDwtIHlfZGF0YVsNCiAgICB4X2RhdGENCiAgICAsDQogICAgLCBvbj0uKHllYXIsIG1vbnRoKSwgbm9tYXRjaD1OVUxMDQogIF1bDQogICAgLCAhYygNCiAgICAgICJjb3VudHJ5IiwgImkuY291bnRyeSIsICJjbGFzcyIsICJtYXR1cml0eSIsICJ5ZWFyIiwgIm1vbnRoIiwgImluZGV4X25hdiIsICJpLmRhdGUiDQogICAgKQ0KICBdDQoNCiAgaGFzX2ludmFsaWQgPC0gcm93U3Vtcyhpcy5uYShtb2RlbF9kYXRhWyAsIC4uZzdfZmVhdHVyZXNdKSkgPiAwDQogIHByaW50KHBhc3RlKGFzc2V0WyJpZCJdLCAicm93czoiLCBucm93KG1vZGVsX2RhdGEpLCAiaW52YWxpZDoiLCBzdW0oaGFzX2ludmFsaWQpKSkNCg0KICBtb2RlbF9kYXRhIDwtIG1vZGVsX2RhdGFbIWhhc19pbnZhbGlkXQ0KICBwcmludChwYXN0ZSgiY2xlYW4gcm93czoiLCBucm93KG1vZGVsX2RhdGEpKSkNCiAgDQogIGFzc2V0X21vZGVscyA8LSBsYXBwbHkod2hpY2goc2FwcGx5KGc3X21vZGVscw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIGZ1bmN0aW9uKHgpIHgkbWF0dXJpdHk9PWFzLm51bWVyaWMoYXNzZXRbIm1hdHVyaXR5Il0pKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAsIGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGc3X21vZGVsc1tbeF1dIA0KICAgICAgICAgICAgICAgICAgICAgICAgIH0pDQogIA0KICByZXMgPC0gbGFwcGx5KGFzc2V0X21vZGVscywgZnVuY3Rpb24gKG0pIHsNCiAgICBwcmludChwYXN0ZShhc3NldFsiaWQiXSwgbSRuYW1lKSkNCiAgICB5IDwtIG1vZGVsX2RhdGFbICwgaW5kZXhfcmV0XQ0KICAgIFggPC0gY2JpbmQoaW50ZXJjZXB0PTEsIG1vZGVsX2RhdGFbICwgbSRmZWF0dXJlcywgd2l0aD1GXSkNCiAgICANCiAgICBBZXEgPC0gYygwLCByZXAoMSwgbmNvbChYKSAtIDEpKQ0KICAgIGJlcSA8LSAxDQogICAgbGIgPC0gYyhjYWltOjpORUdfSU5GLCByZXAoMCwgbmNvbChYKSAtIDEpKQ0KICAgIHd0cyA8LSBjYWltOjpkX2V4cChjYWltOjpoYWxmbGlmZTJkZWNheSgxMiksIG5yb3coWCkpDQoNCiAgICBmaXhlZF9jb2VmcyA8LSBOVUxMDQogICAgaWYgKG0kdW5pdHlfY29lZnMpDQogICAgICBmaXhlZF9jb2VmcyA8LSBjKDAsIHJlcCgxL2xlbmd0aChtJGZlYXR1cmVzKSwgbGVuZ3RoKG0kZmVhdHVyZXMpKSkNCg0KICAgIGxtcSA8LSBjYWltOjpsbV9xdWFkKHksIFgsIEFlcT1BZXEsIGJlcT1iZXEsIGxiPWxiLCB3dHM9d3RzLCBmaXhlZF9jb2Vmcz1maXhlZF9jb2VmcykNCiAgICAjICMgY29lZnMgPC0gcm91bmQobG1xJGNvZWZzLCA2KQ0KICAgICMgIyBwcmludChwYXN0ZSh4LCBjb2VmcykpDQoNCiAgICAjIHByaW50KGxtcSRjb2VmcykNCiAgICAjIHJldHVybihyb3VuZChldmFsdWF0ZShsbXEpLCA2KSkNCiAgICANCiAgICBjb2VmcyA8LSByb3VuZChsbXEkY29lZnMsIDYpDQoNCiAgICByZXR1cm4oZGF0YS50YWJsZShhc3NldD1hc3NldFsiaWQiXQ0KICAgICAgICAgICAsIG1vZGVsPW0kbmFtZQ0KICAgICAgICAgICAsIHQocm91bmQoZXZhbHVhdGUobG1xLCB3dHM9d3RzKSwgNikpDQogICAgICAgICAgICwgY29lZnM9bGlzdChjb2VmcykNCiAgICAgICAgICAgLCBmZWF0dXJlcz1saXN0KG5hbWVzKGNvZWZzKSkNCiAgICAgICAgICApKQ0KICAgICMgcmV0dXJuKGxpc3QobmFtZT1tJG5hbWUsIGNvZWZzPXJvdW5kKGxtcSRjb2VmcywgNiksIGV2YWw9cm91bmQoZXZhbHVhdGUobG1xKSwgNikpKQ0KICAgICMgcmV0dXJuKG5yb3coWCkpDQogIH0pDQoNCiAgIyB5IDwtIG1vZGVsX2RhdGFbICwgaW5kZXhfcmV0XQ0KICAjIFggPC0gY2JpbmQoaW50ZXJjZXB0PTEsIG1vZGVsX2RhdGFbICwgbW9kZWwkZmVhdHVyZXNdKQ0KDQogIHJldHVybihyZXMpDQoNCn0pDQoNCiMgY29tYmluZSBldmFsX2xpc3QsIHdoaWNoIGlzIGEgW1tudW1hc3NldHNdXVtbbnVtbW9kZWxzXV0gbGlzdA0KaGlzdF9ldmFsIDwtIHJiaW5kbGlzdChsYXBwbHkoaGlzdF9ldmFsX2xpc3QsIHJiaW5kbGlzdCkpDQpoaXN0X2V2YWxbXQ0KYGBgDQoNCiMjIyBLLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbg0KYGBge3J9DQprX2ZvbGRfZXZhbCA8LSByYmluZGxpc3QoYXBwbHkoZzdfYXNzZXRzLCAxLCBmdW5jdGlvbihhc3NldCkgew0KICB5X2RhdGEgPC0gZzdfYXNzZXRfZGF0YVtpZD09YXNzZXRbImlkIl1dDQogIHhfZGF0YSA8LSBnN19yZXR1cm5fZGF0YVtjb3VudHJ5PT1hc3NldFsiY291bnRyeSJdXVsNCiAgICAsIGMoInllYXIiLCAibW9udGgiKSA6PSAuKA0KICAgICAgbHVicmlkYXRlOjp5ZWFyKGRhdGUpDQogICAgICAsIGx1YnJpZGF0ZTo6bW9udGgoZGF0ZSkNCiAgICApDQogIF0NCiAgIyBwcmludChwYXN0ZShhc3NldFsiY291bnRyeSJdLCBhc3NldFsibWF0dXJpdHkiXSwgbnJvdyh5X2RhdGEpLCBucm93KHhfZGF0YSkpKQ0KICBtb2RlbF9kYXRhIDwtIHlfZGF0YVsNCiAgICB4X2RhdGENCiAgICAsDQogICAgLCBvbj0uKHllYXIsIG1vbnRoKSwgbm9tYXRjaD1OVUxMDQogIF1bDQogICAgLCAhYygNCiAgICAgICJjb3VudHJ5IiwgImkuY291bnRyeSIsICJjbGFzcyIsICJtYXR1cml0eSIsICJ5ZWFyIiwgIm1vbnRoIiwgImluZGV4X25hdiIsICJpLmRhdGUiDQogICAgKQ0KICBdDQoNCiAgaGFzX2ludmFsaWQgPC0gcm93U3Vtcyhpcy5uYShtb2RlbF9kYXRhWyAsIC4uZzdfZmVhdHVyZXNdKSkgPiAwDQogICMgcHJpbnQocGFzdGUoYXNzZXRbImlkIl0sICJyb3dzOiIsIG5yb3cobW9kZWxfZGF0YSksICJpbnZhbGlkOiIsIHN1bShoYXNfaW52YWxpZCkpKQ0KDQogIG1vZGVsX2RhdGEgPC0gbW9kZWxfZGF0YVshaGFzX2ludmFsaWRdDQogICMgcHJpbnQocGFzdGUoImNsZWFuIHJvd3M6IiwgbnJvdyhtb2RlbF9kYXRhKSkpDQogIA0KICBrX2l4IDwtIGNhaW06OmtfZm9sZF9peChucm93KG1vZGVsX2RhdGEpLCBOVU1fS19GT0xEUykNCiAgDQogIGFzc2V0X21vZGVscyA8LSBsYXBwbHkod2hpY2goc2FwcGx5KGc3X21vZGVscw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIGZ1bmN0aW9uKHgpIHgkbWF0dXJpdHk9PWFzLm51bWVyaWMoYXNzZXRbIm1hdHVyaXR5Il0pKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAsIGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGc3X21vZGVsc1tbeF1dIA0KICAgICAgICAgICAgICAgICAgICAgICAgIH0pDQogIA0KICBtb2RlbF9yZXMgPC0gbGFwcGx5KGFzc2V0X21vZGVscywgZnVuY3Rpb24gKG0pIHsNCiAgICBwcmludChwYXN0ZShhc3NldFsiaWQiXSwgbSRuYW1lKSkNCiAgICB5IDwtIG1vZGVsX2RhdGFbICwgaW5kZXhfcmV0XQ0KICAgIFggPC0gY2JpbmQoaW50ZXJjZXB0PTEsIG1vZGVsX2RhdGFbICwgbSRmZWF0dXJlcywgd2l0aD1GXSkNCiAgICANCiAgICBrX3JlcyA8LSBzYXBwbHkoc2VxKDEsIE5VTV9LX0ZPTERTKSwgZnVuY3Rpb24oZm9sZCkgew0KICAgICAgdHJhaW5faXggPC0ga19peFtrICE9IGZvbGQsIGl4XQ0KICAgICAgdGVzdF9peCA8LSBrX2l4W2sgPT0gZm9sZCwgaXhdDQogICAgICANCiAgICAgIHRyYWluX3kgPC0geVt0cmFpbl9peF0NCiAgICAgIHRyYWluX1ggPC0gWFt0cmFpbl9peF0NCg0KICAgICAgQWVxIDwtIGMoMCwgcmVwKDEsIG5jb2wodHJhaW5fWCkgLSAxKSkNCiAgICAgIGJlcSA8LSAxDQogICAgICBsYiA8LSBjKGNhaW06Ok5FR19JTkYsIHJlcCgwLCBuY29sKHRyYWluX1gpIC0gMSkpDQogICAgICB3dHMgPC0gY2FpbTo6ZF9leHAoY2FpbTo6aGFsZmxpZmUyZGVjYXkoMTIpLCBucm93KHRyYWluX1gpKQ0KICANCiAgICAgIGZpeGVkX2NvZWZzIDwtIE5VTEwNCiAgICAgIGlmIChtJHVuaXR5X2NvZWZzKQ0KICAgICAgICBmaXhlZF9jb2VmcyA8LSBjKDAsIHJlcCgxL2xlbmd0aChtJGZlYXR1cmVzKSwgbGVuZ3RoKG0kZmVhdHVyZXMpKSkNCiAgICAgIA0KICAgICAgbG1xIDwtIGNhaW06OmxtX3F1YWQodHJhaW5feSwgdHJhaW5fWA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgLCBBZXE9QWVxDQogICAgICAgICAgICAgICAgICAgICAgICAgICAsIGJlcT1iZXENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICwgbGI9bGINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICwgd3RzPXd0cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgLCBmaXhlZF9jb2Vmcz1maXhlZF9jb2Vmcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KICAgICAgDQogICAgICB0ZXN0X3kgPC0geVt0ZXN0X2l4XQ0KICAgICAgdGVzdF9YIDwtIFhbdGVzdF9peF0NCiAgICAgIA0KICAgICAgIyBwcmludCh0ZXN0X1gpDQogICAgICB0ZXN0X3lfaGF0IDwtIGFzLm1hdHJpeCh0ZXN0X1gpICUqJSBsbXEkY29lZnMNCiAgICAgIHJlc2lkdWFscyA8LSB0ZXN0X3lfaGF0IC0gdGVzdF95DQogICAgICBSMiA8LSBjb3IodGVzdF95LCB0ZXN0X3lfaGF0KSBeIDINCiAgICAgIFJNU0UgPC0gc3FydChtZWFuKHJlc2lkdWFsc14yKSkNCiAgICAgIGFscGhhX3kgPC0gKDEgKyBtZWFuKHJlc2lkdWFscykpIF4gMTIgLSAxDQogICAgICB0ZV95IDwtIHNkKHJlc2lkdWFscykgKiBzcXJ0KDEyKQ0KDQogICAgICByZXR1cm4ocm91bmQoYyhrPWZvbGQsIFIyPVIyLCBSTVNFPVJNU0UsIGFscGhhX3k9YWxwaGFfeSwgdGVfeT10ZV95KSwgNikpDQoNCiAgICB9KQ0KICAgIA0KICAgIHJldHVybihkYXRhLnRhYmxlKGFzc2V0PWFzc2V0WyJpZCJdLCBtb2RlbD1tJG5hbWUsIHR5cGU9bSR0eXBlLCB0KGtfcmVzKSkpDQoNCiAgfSkNCg0KICByZXR1cm4ocmJpbmRsaXN0KG1vZGVsX3JlcykpDQoNCn0pKVsNCiAgLCAuKA0KICAgIFIyX21lYW4gPSBtZWFuKFIyKQ0KICAgICwgUk1TRV9tZWFuID0gbWVhbihSTVNFKQ0KICAgICwgYWxwaGFfeV9tZWFuID0gbWVhbihhbHBoYV95KQ0KICAgICwgdGVfeV9tZWFuID0gbWVhbih0ZV95KQ0KICAgICwgUjJfc2QgPSBzZChSMikNCiAgICAsIFJNU0Vfc2QgPSBzZChSTVNFKQ0KICAgICwgYWxwaGFfeV9zZCA9IHNkKGFscGhhX3kpDQogICAgLCB0ZV95X3NkID0gc2QodGVfeSkNCiAgICAsIHNjb3JlID0gbWVhbihSTVNFKSAqIHNkKFJNU0UpDQogICkNCiAgLCBieT0uKGFzc2V0LCBtb2RlbCwgdHlwZSkNCiAgXVsNCiAgICBnN19hc3NldHMsIG9uPWMoYXNzZXQ9ImlkIikNCiAgXVsNCiAgICAsIC5TRA0KICAgICwga2V5PS4oY291bnRyeSwgbWF0dXJpdHksIHNjb3JlKQ0KICBdDQoNCmtfZm9sZF9iZXN0X21vZGVscyA8LSBrX2ZvbGRfZXZhbFsNCiAgLCBoZWFkKC5TRCwgMykNCiAgLCBieT0uKGNvdW50cnksIG1hdHVyaXR5LCBhc3NldCkNCiAgXQ0KDQpiZXN0X21vZGVsX2ZyZXEgPC0gc29ydCh0YWJsZShrX2ZvbGRfYmVzdF9tb2RlbHMkdHlwZSksIGRlY3JlYXNpbmc9VCkNCmJlc3RfbW9kZWxfZnJlcQ0KYGBgDQoNCiMjIyBPbGQgYXBwZW5kaXg6IGNvbnN0cmFpbmVkIGxpbmVhciBsZWFzdCBzcXVhcmVzID0gcXVhZHJhdGljIHByb2dyYW1taW5nDQpgYGB7cn0NCiMgdChzYXBwbHkobmFtZXMoZzEzX21vZGVscyksIGZ1bmN0aW9uKHgpIHsNCiMgICBtb2RlbCA8LSBnMTNfbW9kZWxzW1t4XV0NCiMgICB5IDwtIG1vZGVsX2RhdGFbICwgaW5kZXhfcmV0XQ0KIyAgIFggPC0gY2JpbmQoaW50ZXJjZXB0PTEsIG1vZGVsX2RhdGFbICwgbW9kZWwkZmVhdHVyZXMsIHdpdGg9Rl0pDQojIA0KIyAgIEFlcSA8LSBjKDAsIHJlcCgxLCBuY29sKFgpIC0gMSkpDQojICAgYmVxIDwtIDENCiMgICBsYiA8LSBjKGNhaW06Ok5FR19JTkYsIHJlcCgwLCBuY29sKFgpIC0gMSkpDQojICAgd3RzIDwtIGNhaW06OmRfZXhwKGNhaW06OmhhbGZsaWZlMmRlY2F5KDEyKSwgbnJvdyhYKSkNCiMgICANCiMgICBmaXhlZF9jb2VmcyA8LSBOVUxMDQojICAgaWYgKG1vZGVsJHVuaXR5X2NvZWZzKQ0KIyAgICAgZml4ZWRfY29lZnMgPC0gYygwLCByZXAoMS9sZW5ndGgobW9kZWwkZmVhdHVyZXMpLCBsZW5ndGgobW9kZWwkZmVhdHVyZXMpKSkNCiMgDQojICAgbG1xIDwtIGNhaW06OmxtX3F1YWQoeSwgWCwgQWVxPUFlcSwgYmVxPWJlcSwgbGI9bGIsIHd0cz13dHMsIGZpeGVkX2NvZWZzPWZpeGVkX2NvZWZzKQ0KIyAgICMgY29lZnMgPC0gcm91bmQobG1xJGNvZWZzLCA2KQ0KIyAgICMgcHJpbnQocGFzdGUoeCwgY29lZnMpKQ0KIyAgIA0KIyAgIHByaW50KGxtcSRjb2VmcykNCiMgICByZXR1cm4ocm91bmQoZXZhbHVhdGUobG1xKSwgNikpDQojICAgIyByZXR1cm4oYXMubWF0cml4KGMobW9kZWw9eCwgdChyb3VuZChldmFsdWF0ZShsbXEpLCA2KSkpKSkNCiMgfSkpDQoNCmBgYA0KDQo=